c# - 事件处理程序和协方差

标签 c# events delegates variance

我一直在尝试创建一个通用事件。基本上它应该是这样的:

namespace DelegateTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var lol = new SomeClass();
            lol.SomeEvent += handler;
        }

        static void handler(object sender, SomeDerivedClass e)
        {

        }

    }

    class SomeClass
    {

        public delegate void SomeEventDelegate<in T>(object sender, T data);
        public event SomeEventDelegate<ISomeInterface> SomeEvent;

    }

    interface ISomeInterface
    {
    }

    class SomeDerivedClass : ISomeInterface
    {
    }
}

我想允许用户传递第二个参数派生自“ISomeInterface”的任何委托(delegate)。

“in”指定反方差,对吗?这意味着如果 API 期望更通用的东西,您可以传递更具体的东西(在我的基础中,“ISomeInterface”将是通用的,而我的“SomeDerivedClass”将是特定的。) 然而,我被告知我的编译器“没有方法处理程序的重载匹配 DelegateTest.SomeClass.SomeEventDelegate”。

我想知道为什么这不起作用。如果是会造成什么问题?还是我遗漏了一些它无法正常工作的东西?

提前致谢!

最佳答案

"in" specifies contra-variance, right?

是的。

That means if the API is expecting something more general, you can pass it something more specific (in my base "ISomeInterface" would be general, and my "SomeDerivedClass" would be specific).

没有。委托(delegate)逆变允许委托(delegate)引用具有比委托(delegate)类型派生更少的参数类型的方法。例如,假设 ISomeInterface 有一个基本接口(interface):

interface ISomeBaseInterface
{
}

interface ISomeInterface : ISomeBaseInterface
{
}

假设 handler 使用 ISomeBaseInterface 而不是 SomeDerivedClass:

static void handler(object sender, ISomeBaseInterface e) 

然后 new SomeClass().SomeEvent += handler 就可以了。

这就是原始代码类型不安全的原因:当 SomeClass 引发 SomeEvent 时,它可能会传递实现 任何 ISomeInterface 作为 data 参数。例如,它可以传递 SomeDerivedClass 的实例,但也可以传递

的实例
class SomeOtherDerivedClass : ISomeInterface
{
}

如果您能够向事件注册 void handler(object sender, SomeDerivedClass e),该处理程序最终会被 SomeOtherDerivedClass 调用,而这不会工作。

总而言之,您可以注册比事件类型更通用的事件处理程序,而不是更具体的事件处理程序。

更新:您评论:

Well, I actually want to iterate through the list and check the types. So if the event was to be fired with a data object of type let's say SomeOtherDerivedObject, then the program would iterate through the list of methods that are subscribed to the event until it finds one that matches the signature (object, SomeOtherDerivedObject). So the event itself would only be used to store, not to actually call the delegates.

我认为 C# 不允许您声明可与任意委托(delegate)类型一起使用的 event。以下是如何编写添加事件处理程序并调用它们的方法:

class SomeClass
{
    private Delegate handlers;

    public delegate void SomeEventDelegate<in T>(object sender, T data);

    public void AddSomeEventHandler<T>(SomeEventDelegate<T> handler)
    {
        this.handlers = Delegate.Combine(this.handlers, handler);
    }

    protected void OnSomeEvent<T>(T data)
    {
        if (this.handlers != null)
        {
            foreach (SomeEventDelegate<T> handler in
                this.handlers.GetInvocationList().OfType<SomeEventDelegate<T>>())
            {
                handler(this, data);
            }
        }
    }
}

关于c# - 事件处理程序和协方差,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9956596/

相关文章:

c# - 空传播运算符和动态变量

C# - Java 的双端队列

c# - 更改 HttpClient 的基址

c# - 添加时出现 DbUpdateConcurrencyException

ios - 委托(delegate)优势

swift - 将委托(delegate)从 NSWindowController 子类传递到我的 ViewController 时出现问题

ios - Dropbox 委托(delegate)方法不是第一次调用

c# - .Net 事件 - 除 onClick 之外的事件中使用 (object sender, EventArgs e) 参数有何用途

javascript - 在事件回调中获取对模型的引用

javascript - 更改按键