来自TryCatchFinally块的意外行为?

时间:2018-08-14 11:20:15

标签: c# .net

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

看这行

_socket.Send(Encoding.UTF8.GetBytes("hello"));

它会引发异常,因为_socket为空,但是_socket在try块中被分配了。如果以某种方式失败了,它将进入catch块,将successful设置为false而不运行它。

但是,即使抛出catch块,它似乎最终仍在运行?在此示例中,它已成功连接,但是_socket我如何将其分配为空,

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"));
        }
    }
}

4 个答案:

答案 0 :(得分:6)

  

但是,即使catch块被抛出,它似乎最终仍在运行?

这就是finally块的工作原理。一个finally块总是 执行,无论是异常情况还是其他情况。

(嗯,也许不是总是 。如果发生电源故障,如果环境以某种方式完全崩溃,如果主机系统以极端的偏见强行终止了进程,等等。但是只要有代码正在执行,finally块将执行。)

代码中有很多事情要注意...

1)停止假设成功,直到成功为止:

var successful = false;

// and in the try block...
_socket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, OnIncomingData, _socket);
successful = true;

2)使用对象之前,先检查其是否为null

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

答案 1 :(得分:2)

您需要知道,finally块是在引发异常之前执行的。

因此,如果有未捕获的异常(SocketException除外),则catch块将不会执行,因此successful在进入时仍为true finally块。

因此,有可能在初始化_socket之前获得异常,然后直接进入finally块并获得另一个异常。

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

答案 2 :(得分:0)

您似乎不了解finally的工作方式。无论finally / try块中发生什么,catch块始终执行 。您可以在returntry中放入catch语句,然后执行finally。这就是它的全部目的。 您可以详细了解in the msdn documentation

我猜测您只需删除finally块即可达到预期的效果。

答案 3 :(得分:0)

finally-block中的try-catch-finally通常用作清理。您希望它始终执行-是否捕获异常与否无关。需要明确的是,如果应用程序以try-block到达finally-block,则它将在执行finally-block和/或try-block之后始终执行catch-block。这包括try-block中是否存在返回

在多线程中可以看到一个例子。 try-block可以获取某个可变对象的锁。即使在计算中出现意外问题(例如catch-blockParseException)的情况下,NullPointerException也是故障保护。 finally-block将包含用于释放可变对象上的锁的逻辑。这很重要,因为它可以避免死锁,因为最终将始终执行。没有此功能,使用try-catch-blocks可能无法进行清理。

可以从Microsoft here中看到带有流的另一个很好的例子。无论上一个块是否正确完成,此方案都利用finally-block来确保关闭流。

关于您的特定示例,我认为代码可以重构一点。

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
    }
}