下面,我编写了一个简单的方法来尝试连接到服务器。直到最近,当我注意到在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"));
}
}
}
答案 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
块始终执行 。您可以在return
或try
中放入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-block
,ParseException
)的情况下,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
}
}