我一直在尝试创建一个通用事件。基本上它应该是这样的:
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/