多播委托C#中的奇怪行为?

时间:2012-12-06 16:46:10

标签: c# .net delegates

我有这个简单的事件:

public class ClassA
{
    public event Func<string, int> Ev;
    public int Do(string l)
    {
        return Ev(l);
    }
}

和2方法:

  static int Display(string k)
        {
            return k.Length;
        }

  static int Display_2(string k)
        {
            return k.Length*10;
        }

我正在注册此活动:

 ClassA a = new ClassA();
 a.Ev += Display;
 a.Ev += Display_2;

现在,我正在执行:

   Console.WriteLine(a.Do("aaa")); 

输出:

enter image description here

什么???

  • 他有调用列表 2 方法!它 运行它们,但为什么它只显示 last 注册的结果?

  • "3"的结果在哪里消失了? (第一次调用)? (虽然display + display_2都已执行...我不希望console.write迭代结果。但也没想到他会决定显示哪个。

编辑:

enter image description here

4 个答案:

答案 0 :(得分:6)

这里有三个方面:

  1. 事件的实施
  2. 委托合并的行为
  3. 调用其调用列表具有多个条目的委托的行为
  4. 对于第1点,您有一个类似字段的事件。 C#4规范的第10.8.1节给出了一个例子,并说明:

      

    Button类的声明之外,Click成员只能在+=-=运算符的左侧使用,如< / p>

    b.Click += new EventHandler(...);
    
         

    将委托附加到Click事件的调用列表

    (强调我的)。该规范还清楚地表明,类似字段的事件会创建一个委托字段,该字段用于类中进行调用。

    更一般地说(第2点),C#4规范第7.8.4节通过++=讨论代表组合:

      

    代表组合。每个委托类型都隐式提供以下预定义运算符,其中D是委托类型:

    D operator +(D x, D y)
    
         

    当两个操作数都属于某个委托类型+时,二进制D operato执行委托组合。 [...跳过xy为空的位... ...否则,操作的结果是一个新的委托,当被调用时,调用第一个操作数然后调用第二个操作数

    (再次强调我的。)

    最后,第3点 - 事件调用和返回值。 C#规范的第15.4节规定:

      

    如果委托调用包含输出参数或返回值,则它们的最终值将来自列表中最后一个委托的调用。

    更一般地说,它取决于事件的实现。如果您使用使用“正常”委托组合/删除步骤的事件实现,则一切都得到保证。如果你开始编写一个做疯狂事情的​​自定义实现,那就不一样了。

答案 1 :(得分:5)

调用多播非空委托会返回已执行的 last 处理程序的值。

你几乎无法控制谁是第一个或最后一个。这是一个糟糕的系统。

这就是为什么大多数事件和代表都会返回void

答案 2 :(得分:4)

作为一般规则,事件返回值没有意义。

如果你想从事件处理程序获取信息,那么事件处理程序改变输入参数会更有意义,或者只是调用触发事件的任何对象的另一个方法来传达其他信息。

通常这甚至都没有出现,因为事件在逻辑上将信息传递给事件处理程序,并且不需要从事件处理程序获取信息 。它老实说是代码味道的标志。事件不应该关心是否有人订阅了它,他们可能是谁,他们可能在做什么,或者即使有任何订阅者。依赖于它们的返回值,然后只会产生过于紧密的耦合。

答案 3 :(得分:3)

事件只是按照它们附加的顺序迭代。因为您使用的是返回值,所以您获得的值是最后一次调用的值。

虽然这不是真正的事件模式。你可能想要更像一些东西:

public class MyEventArgs : EventArgs
{
    public MyEventArgs()
    {
        Results = new List<int>();
    }
    public string InputString{get;set;}
    public List<int> Results{get;set;}
}
public event EventHandler<MyEventArgs> Ev
public int Do(string l)
{
    MyEventArgs e = new MyEventArgs();
    e.InputString = l;
    if(Ev != null) Ev(this, e);
    return e.Results.Sum();
}

然后

static int Display(object sender, MyEventArgs e)
        {
            return e.Results.Add(k.Length);
        }

  static int Display_2(object sender, MyEventArgs e)
        {
            return e.Results.Add(k.Length*10);
        }