C# TryCatchFinally block的意外行为?
下面我编写了一个简单的方法来尝试连接到服务器。直到最近我才注意到在finally块中,_socket为null,这是没有意义的 看这条线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
_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)不要假设成功。我会看看是否可以合理地更新答案。祝您玩得开心,祝您好运:-)