在C#中使用委托

use*_*587 79 .net c# delegates

在C#语言和.NET框架中,您能帮我理解委托吗?我试图检查一些代码,发现我收到的结果对我来说意外.这里是:

class Program
{
    public static int I = 0;

    static Func<string> del = new Func<string>(I.ToString);

    static void Main(string[] args)
    {
        I = 10;
        Console.WriteLine("{0}", del());
    }
}
Run Code Online (Sandbox Code Playgroud)

答案是0,但不是10.为什么?

Dan*_*rth 78

原因如下:

声明委托的方式直接指向ToString静态int实例的方法.它是在创建时捕获的.

正如弗林德伯格在下面的评论中指出的那样,每个代表都有一个目标和一个在目标上执行的方法.

在这种情况下,要执行的ToString方法显然是方法.有趣的部分是执行方法的实例:它是创建时的实例I,这意味着委托不是I用来获取要使用的实例,而是存储对实例本身的引用.

稍后您将更I改为其他值,基本上会为其分配一个新实例.这不会神奇地改变你的委托中捕获的实例,为什么呢?

要获得您期望的结果,您需要将委托更改为:

static Func<string> del = new Func<string>(() => I.ToString());
Run Code Online (Sandbox Code Playgroud)

像这样,委托指向一个匿名方法,ToString该方法在委托执行时对当前I执行.

在这种情况下,要执行的方法是在声明委托的类中创建的匿名方法.实例为null,因为它是静态方法.

看看编译器为代理的第二个版本生成的代码:

private static Func<string> del = new Func<string>(UserQuery.<.cctor>b__0);
private static string cctor>b__0()
{
    return UserQuery.I.ToString();
}
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,它是不正常的方法的东西.在我们的例子中,它返回调用ToString当前实例的结果I.

  • 接收器被装箱这一事实的一个有趣结果是,无法在可以为空的int上创建GetValueOrDefault()的委托,因为装入一个可以为空的int会生成一个盒装的int,而不是一个盒装的可空int,并且一个盒装的int有没有GetValueOrDefault()方法. (10认同)
  • @ user1859587:委托有一个方法和一个目标(实例),如果它捕获你的实例或lambda函数的实例,而它包含对实例的引用则很重要. (3认同)
  • 丹尼尔,只是为了确认弗林德伯格的评论:你的答案是正确的,但你对拳击的评论不是.user1859587是正确的:观察到的行为是委托捕获呼叫接收者这一事实的结果.虽然在int上调用ToString的接收器将是对int变量的引用,但委托无法在堆上放置对int变量的引用; 对*变量*的引用可能只会进入临时存储.所以它做了下一个最好的事情:它将int包装并引用该堆位置. (3认同)