繁体   English   中英

使用泛型类的参数作为事件类型

[英]Using parameter of generic class as type of event

class MyClass<T> {
    public event T MyEvent;
}

错误: CS0066 'MyClass<T>.MyEvent': event must be of a delegate type

好的... C# ≥7.3 允许Delegate作为基类约束 让我们使用它:

class MyClass<T> where T: Delegate {
    public event T MyEvent;
}

错误: CS0066 'MyClass<T>.MyEvent': event must be of a delegate type

什么???

尽管我在 C# 规范中找不到记录在案的限制,但我可以看到在 C#/CLR 中支持此类事件至少存在两个问题,这两个问题都与它的引发方式有关。

第一难点:语言

C# 只允许从声明它的类型中引发事件。 但是,如果您的泛型类甚至不知道它T的参数数量,那么引发事件的代码应该是什么样的?

class MyClass<T> where T: Delegate 
{
    public event T MyEvent;

    public void DoSomething()
    {
        // raise MyEvent here
        MyEvent(/* what goes here? */);
    }
}

当然,您可以使MyClass抽象并说指定T类型的继承者将引发事件。 然而,在我看来,这将是一种非常不一致的语言设计。

第二个难点:在编译器中

CLR 实现运行时泛型。 这意味着,编译器必须为满足通用约束的任何T生成在运行时应该很好的 IL。

引发事件基本上是调用存储在事件字段中的委托。 编译器应生成大致包括以下步骤的 IL:

  • 将委托对象引用压入堆栈
  • 推论点 1
  • 推论点 2
  • ....
  • 推论 N
  • 调用委托的 Invoke 方法

如果委托不是void ,则需要额外的步骤:

  • 从堆栈中弹出返回值并可能将其存储在字段或局部变量中

如您所见,生成的 IL 严格取决于参数的数量以及委托是否为void 因此,这种 IL 对任何Delegate都没有好处。

相比之下

具有通用参数的事件委托是完全可以的,例如:

delegate void MyEventHandler<K, V>(K key, V value);

因为参数的数量以及委托是否为void在编译时是已知的。 在这种情况下,可以生成适用于任何KV的同一组 IL 指令。 在 IL 中, KV作为类型占位符生成,CLR 能够在运行时解析它们。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM