Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/279.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# TryCatchFinally block的意外行为?_C#_.net - Fatal编程技术网

C# TryCatchFinally block的意外行为?

C# TryCatchFinally block的意外行为?,c#,.net,C#,.net,下面我编写了一个简单的方法来尝试连接到服务器。直到最近我才注意到在finally块中,_socket为null,这是没有意义的 看这条线 _socket.Send(Encoding.UTF8.GetBytes("hello")); 它抛出异常,因为_socket为null,但是_socket在try块中被分配。如果它以某种方式失败,那么它将进入catch块,将successful设置为false,并且不运行它 但它似乎最终会运行,即使抛出了catch块?在本例中,它成功连接,但是_socket

下面我编写了一个简单的方法来尝试连接到服务器。直到最近我才注意到在finally块中,_socket为null,这是没有意义的

看这条线

_socket.Send(Encoding.UTF8.GetBytes("hello"));
它抛出异常,因为_socket为null,但是_socket在try块中被分配。如果它以某种方式失败,那么它将进入catch块,将
successful
设置为false,并且不运行它

但它似乎最终会运行,即使抛出了catch块?在本例中,它成功连接,但是_socketme null怎么可能为空,我正在分配它

public void Connect()
{
    var successful = true;

    try
    {

        if (_socket.IsConnected() || _isConnecting)
        {
            return;
        }

        _isConnecting = true;

        _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        _socket.Connect(new IPEndPoint(_ipAddress, _port));
        _socket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, OnIncomingData, _socket);
    }
    catch (SocketException e)
    {
        successful = false;
    }
    finally
    {
        _isConnecting = false;

        if (successful)
        {
            _socket.Send(Encoding.UTF8.GetBytes("hello"));
        }
    }
}
但它似乎最终会运行,即使抛出了catch块

这正是
最终
块的工作方式。始终执行
finally
块,无论是异常情况还是其他情况

(好吧,也许不总是这样。如果出现电源故障,如果环境完全崩溃,如果主机系统以极端偏见强行终止进程,等等。但只要代码正在执行,就会执行
finally
块。)

在你的代码中有很多东西需要注意

1) 在某件事情真正成功之前,停止假设成功:

var successful = false;

// and in the try block...
_socket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, OnIncomingData, _socket);
successful = true;
2) 使用对象之前,请检查对象是否为空:

if (_socket == null)
{
    _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}
if (_socket.IsConnected() || _isConnecting)
{
    return;
}

_isConnecting = true;

_socket.Connect(new IPEndPoint(_ipAddress, _port));
_socket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, OnIncomingData, _socket);

3) 你一开始没有发现异常。您的
catch
块只查找
SocketException
,但您的代码抛出了
NullReferenceException

,您似乎不知道
最终是如何工作的。无论在
try
/
catch
块中发生什么情况,
finally
块始终执行。您可以在
try
catch
中放入
return
语句,然后
最终将执行。这就是它的全部目的。
你可以阅读更多关于它的内容


我猜您只需删除
finally
块就可以达到预期效果。

您需要知道,
finally
块是在抛出异常之前执行的

因此,如果存在未捕获的异常(除了
SocketException
),则不会执行
catch
块,因此
successful
在输入
finally
块时保持
true

所以有可能在初始化
\u socket
之前发生异常,然后直接进入
最后一个块并发生另一个异常


如果您将
catch(SocketException e)
更改为
catch(Exception e)
,它将按照您的预期工作。

try catch finally中的
finally块
通常用作清理。您希望它总是执行—无论是否捕获到异常都是无关紧要的。需要明确的是,如果应用程序到达带有
最终块的
尝试块
,则在执行
尝试块
和/或
捕获块
后,它将始终执行
最终块
。这包括
try块中存在返回时

这方面的一个例子可以在多线程中看到。
try块
可以获得某个可变对象的锁。
catch块
是一种故障保护,仅在计算中出现意外问题时使用(即
ParseException
NullPointerException
)。
finally块将包含释放可变对象锁的逻辑。这是非常重要的,因为它避免了死锁的可能性,因为finally将始终执行。如果没有此功能,则使用
try catch blocks
可能无法进行清理

另一个很好的例子是微软的streams。此场景利用
finally块
确保流关闭,无论前一个块是否正确完成

关于您的具体示例,我认为代码可以重构一点

public bool Connect() {
    if(_socket == null) // Initialize the _socket object
    if(_socket.IsConnected()) return true;

    try {
        _socket.Connect(new IPEndPoint(_ipAddress, _port));
        return true;
    }
    catch(SocketException e) {
        // Error handling such as logging, error prompt, or reconfigure & retry
        return false;
    }
}

public bool Send(string msg) {
    try {
        if(!Connect()) // Throw Exception
        // Insert sending logic
        return true;
    } 
    catch (Exception e) {
        // Error Handling
        return false;
    }
    finally {
        // Maybe insert close socket logic
    }
}

如果(\u socket.IsConnected()
将使您进入
捕获
中,那么最后在
\u socket
空的
。这在
\u socket=new socket(…)
@Rafalon:and,重要提示:
successful=true
aswell@Stefan在我看来,这比他试图访问对象的方法而不知道该方法是否为
null
重要得多
如果套接字为空,则返回false,这不是原因。检查
\u socket.IsConnected()
后,将
\u socket\u设置为新套接字,因此第一次来到这里时,它将为空。(除非其他设置)这应该是答案,尽管它可能需要更好的措辞。我认为这个答案忽略了一点,即除了SocketException之外的任何异常都会导致成功在finally块中保持为真。@spodger:这是一个很好的观点。OP似乎有一些逻辑上的误解。(1)finally
是如何工作的,(2)
NullReferenceException
SocketException
有何不同,(3)在对象初始化之前不要使用几行代码,(4)不要假设成功。我会看看是否可以合理地更新答案。祝您玩得开心,祝您好运:-)