【问题标题】:Using a Multicast Delegate to chain functions使用多播委托链接函数
【发布时间】:2013-02-20 02:16:15
【问题描述】:

我的问题在下面的代码中有详细说明 - 我问这个的原因是我正在试验代表:

//create the delegate          
delegate int del(int x);

class Program {


    static void Main(string[] args) {

        Program p;
        p = new Program();

        del d = p.a;
        d += p.b;
        d += p.c;
        d += p.d;
        d += p.e;
        Console.WriteLine(d(10)); //<<was hoping it would be 10+2+3+4+5+6

        Console.WriteLine("press [enter] to exit");
        Console.ReadLine();
    }

    private int a(int x) { Console.WriteLine("a is called"); return x + 2; }
    private int b(int x) { Console.WriteLine("b is called"); return x + 3; }
    private int c(int x) { Console.WriteLine("c is called"); return x + 4; }
    private int d(int x) { Console.WriteLine("d is called"); return x + 5; }
    private int e(int x) { Console.WriteLine("e is called"); return x + 6; }

} 

返回 16....

所有函数都会触发,因为“a 被调用”等各种消息都会打印到console,但只返回从最后一个函数e 返回的数量 - 我假设它们在后台是被返回但随后被覆盖?

【问题讨论】:

  • 为了获得这种递归,我认为您必须将前一个委托作为参数传递。否则,您只需添加 10 + 6 = 16。
  • @DavinTryon 这不是真正的递归。没有任何函数调用自己;它只是函数的链接。
  • @Servy 是的,更像“嵌套”函数?是否有一个功能语言名称?
  • @DavinTryon ...这正是正在发生的事情 - 它返回 16
  • 另一个问题是异常。如果其中一种方法抛出异常,您将不会以编程方式知道它是哪一种,也不会知道哪些方法未执行。

标签: c# delegates


【解决方案1】:

当您的问题中有像 d 这样的多播委托时,返回值是来自 d 调用列表的 last 方法的返回值.

一般来说,对于多播委托,使用返回类型void 是最自然的。

编译器没有机会猜到您希望得到10+2+3+4+5+6。你没有在任何地方指定它。

您可以将您的委托类型更改为:

delegate void del(int xToAdd, ref int sum);

那么你的方法a,例如,应该是这样的:

private void a(int x, ref int sum) { Console.WriteLine("a is called"); sum += x + 2; }

然后将像这样调用多播委托实例d

int sum = 0;
d(10, ref sum);
Console.WriteLine(sum);

我希望这会有所帮助。

【讨论】:

    【解决方案2】:

    这不是为委托处理返回类型的方式。将会发生的是所有的处理程序将彼此独立地执行,然后随机选择一个(技术上它是最后订阅的处理程序,但你不应该依赖它)返回给调用者调用了委托。

    我强烈建议您不要使用具有返回值的事件(您将此委托视为事件)。这种行为实际上是不可取的。如果你想要一个返回值,确保你的委托总是映射到一个函数是有意义的,不多也不少。

    至于实际生成所需结果,虽然有多种方法,但最好使用更传统的代表集合:

    List<Func<int, int>> functions = new List<Func<int, int>>();
    //populate
    
    int result = functions.Aggregate(10, (total, func) => func(total));
    

    【讨论】:

    • 按顺序调用调用列表中的方法,使用最后一个调用的返回值(不是随机选择的),您可以依赖那。引用 C# 规范:“调用列表中包含多个条目的委托实例通过按顺序同步调用调用列表中的每个方法来进行。” 并进一步返回值 “将来自对列表中最后一个委托的调用。”同一段还保证可以使用ref参数,就像我在回答中的方式一样。
    • @JeppeStigNielsen 是的,我知道。这并没有改变这样一个事实,即依赖它是非常糟糕的做法,不是因为它未定义并且可能会改变,而是因为它并不为人所知,也不是代码读者期望的行为要很好地定义。在多线程环境中推理也变得非常困难,这在处理这个庄园中的事件时尤其常见。我的回答很明确;这确实是可能,这只是一个非常糟糕的主意。
    • 你说得对,这是个坏主意。用于多播的委托类型(意味着要使用委托组合(“加号”)),包括所有用于事件的委托类型,应始终返回 void 并且没有 out 参数。然后可以使用ref 参数从所有方法(事件术语中的“订阅者”)累积/聚合一个值。
    • @JeppeStigNielsen 即使这样我也不鼓励使用。除其他问题外,它将事件与每一个事件处理程序紧密耦合。所有的事件处理程序都需要知道它是一个聚合值,并且他们需要将其视为一个值(而不是不触摸它、将其清除等)。这也意味着事件需要知道它被用于聚合数据,而它通常不需要知道。它还让它负责给它一个种子,并且不允许事件处理程序相互独立。
    • @JeppeStigNielsen 虽然我坚持我所提供的答案作为解决问题的方法,但如果您强烈认为需要使用单个代表,则最好让处理程序关闭一个用作累加器的变量,而不是使用ref 参数,因为它至少可以避免我在之前评论中提到的许多(但不是全部)问题。
    猜你喜欢
    • 2011-01-12
    • 2011-09-11
    • 1970-01-01
    • 2012-08-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多