使用关键字的动态

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设备上非常宝贵的资源).

Ant*_*ram 5

仍然会调用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谋生,所以我的分析可以归类为警告.但是,行为就是这样.


Eri*_*ert 5

我想你可以通过查看反汇编来解决这个问题,但是阅读规范的第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)

很明显,没有任何东西可以双重关闭.