如何在C#中创建回调变量(真正的singlecast委托)



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

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

"委托的调用列表是一组有序的委托,其中列表中的每个元素都只调用委托所代表的方法之一。"

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

这一领域的官方文档非常令人困惑,但有一点很清楚,即最终用户不能从System.Delegate或System.MulticastDelegate派生。那么,该框架是否支持任何方法来创建一个真正的单播委托,该委托可以用作变量来存储对单个回调的引用?

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

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

Delegate的"问题"是MulticastDelegate是一个派生类,因此如果有人将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.");

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

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
}

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

最新更新