EventHandler和协方差 [英] EventHandlers and Covariance

查看:119
本文介绍了EventHandler和协方差的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

 命名空间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>一些事件

}

接口ISomeInterface
{
}

class SomeDerivedClass:ISomeInterface
{

}

我想允许用户传递任何派生第二个参数的代理来自ISomeInterface。



in指定反方差,对吗?这意味着如果API正在期待更广泛的内容,那么您可以将其更具体地传递(在我的基础上,ISomeInterface将是通用的,我的SomeDerivedClass将是具体的。)
然而,我被告知我的编译器方法处理程序没有重载匹配DelegateTest.SomeClass.SomeEventDelegate。



我想知道为什么这不工作。会有什么问题呢?或者我错过了一些工作吗?



提前感谢

解决方案>


in指定对方差异,对吗?



这意味着如果API正在期待一些更通用的东西,那么可以将其更具体化(在我的基础上,ISomeInterface将是通用的,而我的 SomeDerivedClass将是具体的)。


否。委托违规允许代理引用一个方法,该方法的参数类型比派生类型派生得少。例如,假设 ISomeInterface 有一个基本界面:

  interface ISomeBaseInterface 
{
}

接口ISomeInterface:ISomeBaseInterface
{
}

并且假设处理程序采取 ISomeBaseInterface 而不是 SomeDerivedClass

  static void handler(object sender,ISomeBaseInterface e)

然后 new SomeClass()。SomeEvent + =处理程序将工作。 >

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

  class SomeOtherDerivedClass:ISomeInterface 
{
}

如果您可以在事件中注册 void handler(object sender,SomeDerivedClass e),该处理程序将调用 SomeOtherDerivedClass ,这不起作用。



总之,您可以注册比事件类型更常规的事件处理程序,而不是事件类型



更新:您已评论:


嗯,我实际上想要遍历列表并检查类型。所以如果事件被触发了一个类型的数据对象,我们假设SomeOtherDerivedObject,那么程序将遍历订阅事件的方法列表,直到它找到一个与签名(对象,SomeOtherDerivedObject)匹配的方法。所以事件本身只会用于存储,而不是实际调用代表。


我不认为C#可以让你声明一个可以使用任意代理类型的事件。以下是如何编写添加事件处理程序并调用它们的方法:

  class SomeClass 
{
private代表处理人员

public delegate void SomeEventDelegate< in T>(对象发送者,T数据);

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

protected void OnSomeEvent< T>(T data)
{
if(this.handlers!= null)
{
foreach($)








$($)
}
}
}
}


I've been trying to create a generic event. Basically it should look like this:

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
    {
    }
}

I want to allow the user to pass any delegate which's second parameter is derived from "ISomeInterface."

"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.) I am, however, being told my the compiler that "no overload for method handler matches DelegateTest.SomeClass.SomeEventDelegate."

I am wondering why this isn't working. What are the problems that would be caused if it was? Or am I missing something for it to work?

Thanks in advance!

解决方案

"in" specifies contra-variance, right?

Yes.

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).

No. Delegate contravariance allows a delegate to reference a method with parameter types that are less derived than in the delegate type. For example, suppose ISomeInterface had a base interface:

interface ISomeBaseInterface
{
}

interface ISomeInterface : ISomeBaseInterface
{
}

And suppose handler took ISomeBaseInterface instead of SomeDerivedClass:

static void handler(object sender, ISomeBaseInterface e) 

Then new SomeClass().SomeEvent += handler would work.

Here's why the original code isn't type safe: When SomeClass raises SomeEvent, it can potentially pass anything that implements ISomeInterface as the data argument. For example, it could pass an instance of SomeDerivedClass, but it could also pass an instance of

class SomeOtherDerivedClass : ISomeInterface
{
}

If you were able to register void handler(object sender, SomeDerivedClass e) with the event, that handler would wind up being invoked with SomeOtherDerivedClass, which doesn't work.

In summary, you can register event handlers that are more general than the event type, not event handlers that are more specific.

UPDATE: You commented:

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.

I don't think C# lets you declare an event that works with arbitrary delegate types. Here's how you can write methods that add event handlers and invoke them:

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);
            }
        }
    }
}

这篇关于EventHandler和协方差的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆