3

如何在 C# 中创建一个真正的单播委托。即一个委托实例,它可以在其调用列表中引用一个(并且只有一个)方法,因此可以用作回调(对于单个订阅者)而不是事件(可能有很多订阅者)。

该框架具有 System.Delegate 和 System.MulticastDelegate 类,这给人一种错误的印象,即 System.Delegate 是单播,而 System.Multicast 委托添加了多播功能。但是 System.Delegate http://msdn.microsoft.com/en-us/library/system.delegate.aspx的 MSDN 文档表明 System.Delegate 实际上是多播...

“委托的调用列表是一组有序的委托,其中列表的每个元素恰好调用委托所代表的方法之一。”

...而 System.MulticastDelegate 的文档并没有真正解释它提供的额外行为。

这方面的官方文档相当混乱,但有一点很清楚,最终用户不能从 System.Delegate 或 System.MulticastDelegate 派生。那么框架是否支持创建真正的单播委托,该委托可以用作变量来存储对单个回调的引用?

@dtb。如果我可以使用单播委托,则无需运行时检查。当然,应用程序逻辑仍然可能以其他方式失败,例如分配了错误的处理程序,但至少如果我使用单播委托,那么存在多个处理程序的问题我只期望一个是一个简单的问题不可能存在,因此需要检查的东西更少,单元测试更简单,设计更优雅。此外,如果具有返回值的方法的委托在其调用列表中有多个处理程序,则返回给调用者的是列表中最后一个处理程序返回的值,而不是第一个处理程序。

4

2 回答 2

3

如果您真的需要一个带有事件的“单播”委托,为什么不简单地为该事件实现自己的添加/删除方法呢?

的“问题”DelegateMulticastDelegate派生类,因此如果有人将MulticastDelegate对象分配给Delegate变量,那么您将始终拥有一个MulticastDelegate.

例如,我们可以将默认event实现简化为:

private ChangedEventHandler _changed;
public event ChangedEventHandler Changed
{
   add
   {
      _changed += value;
   }
   remove
   {
      _changed -= value;
   }
}

现在让我们将事件实现更改为:

private ChangedEventHandler _changed;
public event ChangedEventHandler Changed
{
   add
   {
      _changed = value; // Do NOT combine delegates
   }
   remove
   {
      _changed -= value;
   }
}

现在你拥有的是(几乎)一个单播委托,因为事件语法用户不能直接分配多播委托,并且只存储最后分配的委托。如果您的用户真的是恶意的,他们可以创建一个MulticastDelegate然后将该委托添加到您的事件处理程序。如果您确实需要防止这种情况,您可以将此检查添加到add方法中:

if (value.GetInvocationList().Length > 1)
    throw new ArgumentException("MulticastDelegates are not allowed here.");
于 2012-09-06T12:55:42.567 回答
1

这比使用委托有点尴尬,但一种方法是使用您想要的方法传递回调接口。由于该方法只能有一个实现,因此只能注册一个回调。

public interface IDoSomething
{
    void DoSomething();
}

public sealed class MyClass
{
    private IDoSomething _doer;

    //We use a Set method rather than a property to prevent other classes from accessing the callback
    //Another common (and generally better) pattern is to pass the instance into the constructor
    public void SetSomethingDoer(IDoSomething doer)
    {
        _doer = doer;
    }

    //Other code can now access _doer to call back the method
}

这具有允许您将多个回调方法组合在一起的附带好处,这在您试图保证单个回调处理程序的情况下通常是有意义的。

于 2012-09-06T15:25:20.593 回答