EventHandlers和Covariance

hai*_*yyu 6 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
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

我想允许用户传递任何委托,其中第二个参数派生自"ISomeInterface".

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

我想知道为什么这不起作用.如果是这样会导致什么问题?或者我错过了一些工作的东西?

提前致谢!

Mic*_*Liu 7

"in"指定反差,对吗?

是.

这意味着如果API期望更通用的东西,你可以传递更具体的东西(在我的基础"ISomeInterface"将是通用的,而我的"SomeDerivedClass"将是特定的).

否.委托反演允许委托引用一种方法,该方法的参数类型少于委托类型.例如,假设ISomeInterface有一个基本接口:

interface ISomeBaseInterface
{
}

interface ISomeInterface : ISomeBaseInterface
{
}
Run Code Online (Sandbox Code Playgroud)

并假设handlerISomeBaseInterface,而不是SomeDerivedClass:

static void handler(object sender, ISomeBaseInterface e) 
Run Code Online (Sandbox Code Playgroud)

然后new SomeClass().SomeEvent += handler会工作.

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

class SomeOtherDerivedClass : ISomeInterface
{
}
Run Code Online (Sandbox Code Playgroud)

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

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

更新:你评论说:

好吧,我实际上想要遍历列表并检查类型.因此,如果要使用类型为letOtherDerivedObject的数据对象触发事件,则程序将遍历订阅该事件的方法列表,直到找到与该签名匹配的方法(对象,SomeOtherDerivedObject).所以事件本身只会用于存储,而不是实际调用代理.

我不认为C#允许你声明一个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);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)