3

对作为参数传递的 C# 委托感到困惑:

class Program
{
    static void Main(string[] args)
    {
        var a = new A();
        Action holder = delegate{};
        //a.Attach1(holder); //nothing printed
        a.Attach2(ref holder);//print as expected

        holder();
    }
}

public class A
{
    private void P1()
    {
        Console.WriteLine("Inaccessible");
    }
    public void P2()
    {
        Console.WriteLine("Accessible");
    }
    public void Attach1(Action holder)
    {
        holder += P1;
        holder += P2;
    }
    public void Attach2(ref Action holder)
    {
        holder += P1;
        holder += P2;
    }
}

委托是引用类型,为什么它仍然需要与字体中的 ref 一起传递才能像在 Attach2 中一样正常工作,比如值类型?

从 C++ 经验来看,delegate 只是一个函数指针,Attach1(Action holder) 类似于 Attach1(Action* holder),原始持有人作为“值”传递,因此未分配,而在第二种情况下,Attach2(ref Action holder) 类似于 Attach1(Action** holder),指针实际上是通过的,因此可以正确操作。但是为什么在 .NET 中没有任何指示或提示???

4

4 回答 4

6

因为委托实例是不可变的,并且+=是对委托实例的新赋值;基本上是:

holder = (Action)Delegate.Combine(holder, P1);
holder = (Action)Delegate.Combine(holder, P2);

如果您不将其传递为ref,则在方法之外将看不到新值。

或者用更简单的术语来说 - 考虑 a string; astring同样是不可变的并且+=是一个赋值。现在考虑:

public void Append(string s) {
    s += "[suffix]";
}
public void Append2(ref string s) {
    s += "[suffix]";
}

如果我们调用:

string x = "abc";
Append(x);
Console.WriteLine(x);

我们会看到abc的。如果我们打电话

string x = "abc";
Append2(ref x);
Console.WriteLine(x);

我们将看到abc[suffix]——出于完全相同的原因

于 2013-06-06T09:06:45.970 回答
2
holder += P1;

这一行有效地创建了一个委托并将其分配给holder变量。它不会修改现有的委托。

所以:

Action holder = delegate{};
a.Attach2(ref holder);

holder(); //<-- holder is no longer the delegate assigned two lines above

当然,您需要使用ref它来完成这项工作,否则内部的赋值Attach2只会影响有效的局部变量。

于 2013-06-06T09:06:04.170 回答
0

MSDN上所述

不要将通过引用传递的概念与引用类型的概念混淆。这两个概念并不相同。方法参数无论是值类型还是引用类型,都可以通过 ref 进行修改。通过引用传递值类型时没有装箱。

请参阅该链接上的第二个示例,它解释了问题

于 2013-06-06T09:13:48.737 回答
0

我想你只是在试验这个。您通常会发送要添加到成员委托的函数,然后该成员将是由 += 返回的新对象

public class Program
{
    static void Main(string[] args)
    {
        var a = new A();
        a.Attach();
        a.doSomething();
    }
}

public class A
{
    public delegate void handler();
    public handler doSomething;
    private void P1()
    {
        Console.WriteLine("Inaccessible");
    }
    public void P2()
    {
        Console.WriteLine("Accessible");
    }
    public void Attach()
    {
        doSomething += P1;
        doSomething += P2;
    }
}
于 2013-06-07T08:25:09.677 回答