Ang*_*ker 5 c# programming-languages using .net-micro-framework
请考虑以下代码:
// module level declaration
Socket _client;
void ProcessSocket() {
_client = GetSocketFromSomewhere();
using (_client) {
DoStuff(); // receive and send data
Close();
}
}
void Close() {
_client.Close();
_client = null;
}
Run Code Online (Sandbox Code Playgroud)
鉴于代码调用Close()方法,关闭_client套接字并将其设置为null,同时仍然在"using"块内,幕后究竟发生了什么?套接字真的关闭了吗?有副作用吗?
PS这是在.NET MicroFramework上使用C#3.0,但我认为c#(语言)应该具有相同的功能.我问的原因是偶尔,很少,我用完套接字(这是.NET MF设备上非常宝贵的资源).
仍然会调用Dispose.您所做的只是将变量_client指向内存中的其他内容(在本例中为null)._client最初引用的对象仍将在using语句的末尾处理.
运行此示例.
class Program
{
static Foo foo = null;
static void Main(string[] args)
{
foo = new Foo();
using (foo)
{
SomeAction();
}
Console.Read();
}
static void SomeAction()
{
foo = null;
}
}
class Foo : IDisposable
{
#region IDisposable Members
public void Dispose()
{
Console.WriteLine("disposing...");
}
#endregion
}
Run Code Online (Sandbox Code Playgroud)
将变量设置为null不会破坏对象或阻止对象使用.您所做的只是更改变量的引用,而不是更改最初引用的对象.
晚编辑:
关于MSDN使用参考http://msdn.microsoft.com/en-us/library/yh598w02.aspx的评论和OP中的代码以及我的示例中的讨论的讨论,我创建了一个更简单的代码版本,就像这样.
Foo foo = new Foo();
using (foo)
{
foo = null;
}
Run Code Online (Sandbox Code Playgroud)
(而且,是的,对象仍然被处理掉了.)
您可以从上面的链接推断出代码正在被重写如下:
Foo foo = new Foo();
{
try
{
foo = null;
}
finally
{
if (foo != null)
((IDisposable)foo).Dispose();
}
}
Run Code Online (Sandbox Code Playgroud)
哪个不会处置该对象,这与代码片段的行为不匹配.所以我通过ildasm看了一下,我能收集的最好的是原始引用被复制到内存中的新地址.该语句foo = null;适用于原始变量,但调用.Dispose()发生在复制的地址上.所以这里看看我是如何相信代码实际上被重写的.
Foo foo = new Foo();
{
Foo copyOfFoo = foo;
try
{
foo = null;
}
finally
{
if (copyOfFoo != null)
((IDisposable)copyOfFoo).Dispose();
}
}
Run Code Online (Sandbox Code Playgroud)
作为参考,这是IL通过ildasm看起来的样子.
.method private hidebysig static void Main() cil managed
{
.entrypoint
// Code size 29 (0x1d)
.maxstack 1
.locals init ([0] class Foo foo,
[1] class Foo CS$3$0000)
IL_0000: newobj instance void Foo::.ctor()
IL_0005: stloc.0
IL_0006: ldloc.0
IL_0007: stloc.1
.try
{
IL_0008: ldnull
IL_0009: stloc.0
IL_000a: leave.s IL_0016
} // end .try
finally
{
IL_000c: ldloc.1
IL_000d: brfalse.s IL_0015
IL_000f: ldloc.1
IL_0010: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0015: endfinally
} // end handler
IL_0016: call int32 [mscorlib]System.Console::Read()
IL_001b: pop
IL_001c: ret
} // end of method Program::Main
Run Code Online (Sandbox Code Playgroud)
我没有盯着ildasm谋生,所以我的分析可以归类为警告.但是,行为就是这样.
我想你可以通过查看反汇编来解决这个问题,但是阅读规范的第8.13节要容易得多,其中清楚地描述了所有这些规则.
阅读这些规则可以清楚地表明代码
_client = GetSocketFromSomewhere();
using (_client)
{
DoStuff();
Close();
}
Run Code Online (Sandbox Code Playgroud)
由编译器转换为
_client = GetSocketFromSomewhere();
{
Socket temp = _client;
try
{
DoStuff();
Close();
}
finally
{
if (temp != null) ((IDispose)temp).Dispose();
}
}
Run Code Online (Sandbox Code Playgroud)
这就是发生的事情.套接字在非特殊代码路径中被丢弃两次.这对我来说可能不是致命的,但绝对是一种难闻的气味.我写这个:
_client = GetSocketFromSomewhere();
try
{
DoStuff();
}
finally
{
Close();
}
Run Code Online (Sandbox Code Playgroud)
很明显,没有任何东西可以双重关闭.