代表方差规则的怪异示例 [英] Weird example of variance rules for delegates

查看:98
本文介绍了代表方差规则的怪异示例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在埃里克·利珀特(Eric Lippert)的博客文章中,简称为协方差,协方差或方差,在诸如 C#简而言之之类的书中,则表示:

In Eric Lippert's blog posts about covariance and contravariance or variance for short, and in books such as C# in a Nutshell, it is stated that :


如果要定义泛型委托类型,最好的做法是:

If you’re defining a generic delegate type, it’s good practice to:


  • 标记a类型参数,仅在返回值上用作协变(out)。

  • 将仅类型参数在返回值上用作反变量(in)。

这样做可以通过尊重类型之间的
继承关系来使转换自然地进行。

Doing so allows conversions to work naturally by respecting inheritance relationships between types.

因此,我正在对此进行试验,并且找到了一个非常奇怪的示例。

So I was experimenting this and I have found a rather strange example.

使用该类层次结构:

class Animal { }

class Mamal : Animal { }
class Reptile : Animal { }

class Dog : Mamal { }
class Hog : Mamal { }

class Snake : Reptile { }
class Turtle : Reptile { }

尝试使用方法组到委托的转换和委托到委托的转换,我编写了以下代码片段:

While trying to play with method-group-to-delegate conversions and delegate-to-delegate conversions, I wrote this code snippet :

 // Intellisense is complaining here  
 Func<Dog, Reptile> func1 = (Mamal d) => new Reptile();

 // A local method that has the same return type and same parameter type as the lambda expression.
 Reptile GetReptile(Mamal d) => new Reptile();

 // Works here.  
 Func<Dog, Reptile> func2 = GetReptile;

为什么方差规则适用于局部方法而不适用于lambda表达式? / em>

Why are the variance rules working for the local method but not for the lambda expression ?

鉴于lambda表达式是一种未命名的方法,它代替了委托实例,并且编译器立即将lambda表达式转换为以下任意一种:

Given that a lambda expression is an unnamed method written in place of a delegate instance and that the compiler immediately converts the lambda expression to either:


  • 委托实例。

  • 表达式树,类型为Expression。

我假设使用:

 Func<Dog, Reptile> func1 = (Mamal d) => new Reptile();

正在发生的事情是由类似的转换:

What is happening is a conversion from something like :

Func<Mamal, Reptile> => Func<Dog, Reptile>. 

从委托人到委托人的方差规则是否不同于从方法组到委托人的方差规则?

Are variance rules from delegates to delegates different from variance rules for method groups to delegates?

推荐答案

让我稍微澄清一下您的问题。

Let me clarify your question slightly.


这三件事可以转换为委托类型:(1)lambda(或C#2样式匿名方法),(2)方法组或本地方法,(3)另一个委托。在每种情况下,哪些协变和协变转换的规则在法律上是否不同?

These three things may be converted to a delegate type: (1) a lambda (or C# 2 style anonymous method), (2) a method group or local method, (3) another delegate. Are the rules for what covariant and contravariant conversions are legal different in each case?

是。


它们有什么不同?

How are they different?

您应该阅读规格以获得确切的详细信息,但简要地:

You should read the specification for the exact details, but briefly:


  • 仅当委托类型参数为通用委托类型时,才可以将其转换为另一种通用委托类型被标记为协变或逆变。也就是说, Func<长颈鹿> 可以转换为 Func<动物< c T> 标记为协变。 (此外:如果您需要从一种委托类型到另一种委托类型进行变体转换,并且该委托类型不支持方差,您可以使用 Invoke 源委托的方法,现在我们使用的是方法组规则,但失去了引用相等性。)

  • A generic delegate type may be converted to another generic delegate type only if the delegate type parameters are marked as covariant or contravariant. That is, Func<Giraffe> can be converted to Func<Animal> because Func<out T> is marked as covariant. (Aside: if you need to make a variant conversion from one delegate type to another and the delegate type does not support variance, what you can do instead is use the method group of the Invoke method of the "source" delegate, and now we're using method group rules, but losing reference equality.)

方法组或本地方法即使未将委托标记为支持方差,也可以使用协方差和逆差规则将其转换为匹配的委托人类型。也就是说,您可以将 Giraffe G()转换为 delegate Animal D(); ,即使 D 不是通用的,或者不是通用的但未标记为变体。

A method group or local method can be converted to a matching delegate type using covariance and contravariance rules, even if the delegate is not marked as supporting variance. That is, you can convert Giraffe G() to delegate Animal D(); even if D is not generic, or is generic but not marked as variant.

转换lambda的规则很复杂。如果lambda没有形式参数类型,则使用目标类型的形式参数类型,对lambda主体进行分析,如果主体进行无错误分析并且结果与目标类型的结果类型。 如果lambda确实具有形式参数类型,则它们必须与目标类型的形式参数类型完全匹配

The rules for converting lambdas are complicated. If the lambda has no formal parameter types, then the formal parameter types of the target type are used, the lambda body is analyzed, and it is convertible if the body analyzes without error and the result is compatible with the target type's result type. If the lambda does have formal parameter types they must match exactly the target type's formal parameter types.


为什么它们不同?

Why are they different?

不同的事物是不同的。我真的不知道该如何回答这样一个模糊,广泛的为什么问题。

Different things are different. I really don't know how to answer such a vague, broad "why" question.

这些规则是由十几个人坐在一个房间里经过一段时间后得出的。很多年。在C#1中添加了用于委托转换的方法组,在C#2中添加了通用委托,在C#3中添加了lambda,在C#4中添加了通用委托方差。我不知道如何回答关于为什么的问题实际上,完成了数百小时的设计工作,其中一半以上是在我加入设计团队之前的。该设计工作涉及许多争论和折衷。 请不要问有关编程语言设计的模糊为什么和为什么不的问题

Those rules were derived by a dozen people sitting in a room over a period of many years. Method group to delegate conversions were added in C# 1, generic delegates were added in C# 2, lambdas were added in C# 3, generic delegate variance was added in C# 4. I have no idea how to possibly answer a "why" question about the literally hundreds of hours of design work that were done, and more than half of it was before I was on the design team. That design work involved a great many arguments and compromises. Please do not ask vague "why" and "why not" questions about programming language design.

诸如规范的哪个页面定义了此行为?之类的问题。有一个答案,但是为什么规范会这样说?基本上是要求对15年前从事此设计工作的人员进行心理分析,以及为什么他们发现某些折衷方案令人信服,而其他折衷方案却没有那么多。我不具备或不愿意进行这种分析;

Questions like "what page of the spec defines this behaviour?" have an answer, but "why does the spec say that?" is basically asking for a psychological analysis of people who did this design work fifteen years ago, and why they found certain compromises compelling and others not so much. I'm not capable or willing to do that analysis; it would involve rehashing literally hundreds of hours of arguments.

如果您的问题是什么是鼓励或不鼓励精确或不精确匹配的通用语言设计原则?我可以长时间讨论这个话题。例如,我设计了一种新的重载解析算法昨天,重载解析只不过是确定精确或不精确匹配的重要性以及它们的重要性提出更具体的问题

If your question is "what are general language design principles which encourage or discourage exact or inexact matching?" that's a topic I could discuss at length for hours. For example, I designed a new overload resolution algorithm yesterday and overload resolution is about nothing other than deciding when exact or inexact matches are important, and how important they are. Ask a more specific question.

告诉您什么,让代替我来完成这项工作。这是您的一种情况:

Tell you what, let's have you do that work instead of me. Here's one of your scenarios:

Action<Mammal> ma = (Animal a) => ...

向我描述禁止用户编写该行的引人注目的好处代码。示例:对我来说确实像是个错误。看起来用户开始输入一件事,并在中途改变了主意。这种毫无意义的,怪异的不一致是草率的,错误的代码的高度特征,可以很容易地避免。 C#的设计原则之一是,该语言会告诉您何时您可能犯了一个错误。那肯定看起来像是个错误。

Describe to me the compelling benefit of disallowing the user from writing that line of code. Example: It sure looks like a bug to me. It looks like the user started typing one thing and changed their mind halfway through. This sort of pointless, bizarre inconsistency is highly characteristic of sloppy, buggy code, and it can be easily prevented. One of the design principles of C# is that the language tells you when you've probably made a mistake. That sure looks like a mistake.

现在,反驳说应该允许代码。示例:就Lambda和本地方法之间的可转换性规则而言,是否也应保持一致是一个普遍原则?与防止草率错误的规则相比,该规则有多重要?

Now make the counter-argument that the code should be allowed. Example: Is it also a general principle that there should be consistency between lambdas and local methods as far as their convertibility rules go? How important is that rule compared to the rule about preventing sloppy bugs?

现在提出了更多关于每种选择的优缺点的论点,以及不同的开发人员方案如何影响您对每个选择的分析。给出许多真实的示例。

请记住,有些用户是类型系统方面的专家,有些则不是。有些是具有二十年经验的建筑师,有些则刚大学毕业。有些Java程序员是昨天才开始使用C#的人,但他们仍然怀着擦除的心态。有些是F#程序员,他们习惯于对程序进行全程序推理。 对每种情况的优缺点进行大量记述,然后提出一个折衷的建议,该建议在任何重要的情况下都不能妥协。

Keep in mind that some users are experts in type systems, some are not. Some are architects with twenty years experience, some are fresh out of college. Some are Java programmers who just picked up C# yesterday and are still in an erasure mindset; some are F# programmers who are used to full-program inference. Take extensive notes on the pros and cons of each scenario, and then come up with a compromise proposal that does not compromise too far on any important scenario.

现在考虑费用。拟议的功能会难以实现吗?是否添加新的错误消息?消息是否清晰,否则会使用户感到困惑?建议的功能可能会阻止将来的任何功能吗?我注意到您必须对语言的未来做出好的预测。

Now consider the costs. Will the proposed feature be difficult to implement? Does it add a new error message? Is the message clear, or will it confuse users? Does the proposed feature possibly prevent any future feature? I note that you have to make good predictions of the future of the language to do this step.

一旦您提出了决定,然后用一句话描述所有工作,回答为什么要决定?这个问题。

Once you've come up with a decision, then describe all that work in one sentence that answers the question "why did you decide that?"

这篇关于代表方差规则的怪异示例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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