事件在.NET 4.0委托逆变和C#4.0 [英] Event and delegate contravariance in .NET 4.0 and C# 4.0

查看:227
本文介绍了事件在.NET 4.0委托逆变和C#4.0的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在调查这个问题我好奇新的协​​方差/逆变功能如何在C#4.0,会影响它。

While investigating this question I got curious about how the new covariance/contravariance features in C# 4.0 will affect it.

在Beta 1中,C#似乎与CLR不同意。早在C#3.0,如果你有:

In Beta 1, C# seems to disagree with the CLR. Back in C# 3.0, if you had:

public event EventHandler<ClickEventArgs> Click;

...然后在其他地方你有:

... and then elsewhere you had:

button.Click += new EventHandler<EventArgs>(button_Click);

...编译器会BARF,因为他们是不兼容的委托类型。但在C#4.0,它编译罚款,因为在CLR 4.0类型参数已被标记为,所以它是逆变的,因此编译器假定组播委托 + = 将工作。

下面是我的测试:

public class ClickEventArgs : EventArgs { }

public class Button
{
    public event EventHandler<ClickEventArgs> Click;

    public void MouseDown()
    {
        Click(this, new ClickEventArgs());
    }
}

class Program
{    
    static void Main(string[] args)
    {
        Button button = new Button();

        button.Click += new EventHandler<ClickEventArgs>(button_Click);
        button.Click += new EventHandler<EventArgs>(button_Click);

        button.MouseDown();
    }

    static void button_Click(object s, EventArgs e)
    {
        Console.WriteLine("Button was clicked");
    }
}

不过,尽管汇编,它在运行时不工作。(的ArgumentException :代表必须是同一类型)

But although it compiles, it doesn't work at runtime (ArgumentException: Delegates must be of the same type).

这没关系,如果你只添加两个委托类型的任何一个。但两种不同类型的多播的组合导致时,第二个加入的异常。

It's okay if you only add either one of the two delegate types. But the combination of two different types in a multicast causes the exception when the second one is added.

我想这是在Beta 1中(编译器的行为看起来有望右)。

I guess this is a bug in the CLR in beta 1 (the compiler's behaviour looks hopefully right).

更新发布候选:

以上code不再编译。它必须是 TEventArgs 在逆变的事件处理程序和LT; TEventArgs&GT; 委托类型已回滚,所以现在说委托具有相同的定义,在.NET 3.5。

The above code no longer compiles. It must be that the contravariance of TEventArgs in the EventHandler<TEventArgs> delegate type has been rolled back, so now that delegate has the same definition as in .NET 3.5.

这是,我看着都必须为测试版:

That is, the beta I looked at must have had:

public delegate void EventHandler<in TEventArgs>(object sender, TEventArgs e);

现在又回到了:

public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);

动作&LT; T&GT; 委托参数 T 仍逆变:

public delegate void Action<in T>(T obj);

这同样适用于 Func键&LT; T&GT; T 是协

这使得妥协有很大的意义,只要我们假定多点传送代表的主要用途是在事件的背景。我个人发现,我从来没有使用多路广播委托,除非事件。

This compromise makes a lot of sense, as long as we assume that the primary use of multicast delegates is in the context of events. I've personally found that I never use multicast delegates except as events.

所以我想C#编码标准,现在可以采取一种新的规则:不要形成了从通过协方差/逆变多个相关的委托类型多路广播委托。如果你不知道这是什么意思,只是避免使用动作事件是在安全方面。

So I guess C# coding standards can now adopt a new rule: don't form multicast delegates from multiple delegate types related through covariance/contravariance. And if you don't know what that means, just avoid using Action for events to be on the safe side.

当然,这一结论对原来的问题,这其中来自 ...

Of course, that conclusion has implications for the original question that this one grew from...

推荐答案

很有意思。你并不需要用事件来看到这种情况出现,而事实上我觉得它更容易使用简单的代表。

Very interesting. You don't need to use events to see this happening, and indeed I find it simpler to use simple delegates.

考虑 Func键&LT;串GT; Func键&LT;对象&gt; 。在C#4.0中,您可以隐式转换 Func键&LT;串&GT; Func键&LT;对象&gt; ,因为你总是可以使用一个字符串参照作为对象引用。但是,当你试图把它们混合起来的东西出问题。下面是一个简短但完整的节目展示了两种不同方式的问题:

Consider Func<string> and Func<object>. In C# 4.0 you can implicitly convert a Func<string> to Func<object> because you can always use a string reference as an object reference. However, things go wrong when you try to combine them. Here's a short but complete program demonstrating the problem in two different ways:

using System;

class Program
{    
    static void Main(string[] args)
    {
        Func<string> stringFactory = () => "hello";
        Func<object> objectFactory = () => new object();

        Func<object> multi1 = stringFactory;
        multi1 += objectFactory;

        Func<object> multi2 = objectFactory;
        multi2 += stringFactory;
    }    
}

这编译好,但无论是联合电话(由+ =语法糖隐藏)的抛出异常。 (注释掉第一个看到第二个。)

This compiles fine, but both of the Combine calls (hidden by the += syntactic sugar) throw exceptions. (Comment out the first one to see the second one.)

这肯定是有问题,虽然我不能完全确定该解决方案应该是什么。这有可能是在执行时的委托code将需要制定出最合适的类型根据所涉及的委托类型使用。这是一个有点讨厌。这将是相当不错的有一个通用的 Delegate.Combine 电话,但你不能真正的前preSS相关类型以有意义的方式。

This is definitely a problem, although I'm not exactly sure what the solution should be. It's possible that at execution time the delegate code will need to work out the most appropriate type to use based on the delegate types involved. That's a bit nasty. It would be quite nice to have a generic Delegate.Combine call, but you couldn't really express the relevant types in a meaningful way.

有一件事值得一提的是,协变转换是引用转换 - 在上面, multi1 stringFactory 指的是同一个对象:它的的同写

One thing that's worth noting is that the covariant conversion is a reference conversion - in the above, multi1 and stringFactory refer to the same object: it's not the same as writing

Func<object> multi1 = new Func<object>(stringFactory);

(在这一点上,下面一行将不例外执行)在执行时,首创置业确实不得不面对一个 Func键&LT;串&GT; Func键&LT;对象&gt; 进行组合;它没有其它信息去。

(At that point, the following line will execute with no exception.) At execution time, the BCL really does have to deal with a Func<string> and a Func<object> being combined; it has no other information to go on.

这是肮脏的,我认真地希望它被固定在某个方面。我要提醒的Mads和Eric这个问题,所以我们可以得到一些更明智的评论。

It's nasty, and I seriously hope it gets fixed in some way. I'll alert Mads and Eric to this question so we can get some more informed commentary.

这篇关于事件在.NET 4.0委托逆变和C#4.0的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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