在==操作符重载解析与变异泛型委托类型 [英] Overload resolution on operator == with variant generic delegate types
问题描述
什么是重载的精确的规则与 ==
委托类型的两个表达式?
考虑下面的代码(其中使用系统;
需要):
静类ProgramA
{
静态无效TargetMethod(obj对象)
{
}
静态无效的主要()
{
动作<对象>实例1 = TargetMethod;
动作<对象> INSTANCE2 = TargetMethod;
动作<串GT; A1 = INSTANCE1;
动作<&乌里GT; A2 = INSTANCE2;
Console.WriteLine((对象)A1 ==(对象)A2);
Console.WriteLine((代表)A1 ==(代表)A2);
Console.WriteLine((动作<对象>)A1 ==(动作<对象>)A2);
Console.WriteLine(A1 == A2); //警告CS0253:可能无意引用比较;得到一个值比较,铸就右侧键入'System.Action<串>'
}
}
说明:
实例1
和 INSTANCE2
是相同的运行时类型的两个独立的情况下,一般动作<在T>
这就是逆变在 T
。这些情况是不同的,但等于
,因为它们的拥有相同的目标。
A1
和 A2
相同实例1
和 INSTANCE2
,但因为动作<的逆变;而在T>
存在的隐的从动作<对象> ;
到每个动作<串>
和动作<的System.Uri方式>
现在,在C#语言规范有(除其他重载)这些运算符==
:
布尔运算符==(对象x,对象Y); //§7.10.6
布尔运算符==(System.Delegate x,System.Delegate y)的; //§7.10.8
目前的Visual C#编译器来实现,如果所引用检查只是第一个是相同的(白细胞介素实际上不叫喜欢 object.ReferenceEquals
A mscorlib程序方法,但这样会产生相同的结果),同时通过调用<一个实现第二个HREF =http://msdn.microsoft.com/en-us/library/system.delegate.op_equality.aspx> Delegate.op_Equality
方法其中看起来,即使它是由C#语言规范中定义的组件内用户自定义操作符,所以也许不是用户自定义中的规范(?)的感觉。
注意§7.10.8是有点混乱,因为它说的每个委托类型都隐式提供下列预定义的比较运算符[S]的,然后给出了与运营商的(System.Delegate,System.Delegate)
签名。这只是的有一个的运营商,而不是一个每委托类型?这似乎是我的问题很重要。
这是不奇怪的三首的WriteLine
写假
,真
和真
,分别给予了我上面说的。
的 问:的但是为什么第四的WriteLine
导致所使用的(对象,对象)
超载?
有确实存在的隐式引用转换动作<>
(或任何其他委托类型)为 System.Delegate
,那么为什么不能在这里使用?重载决策应该更喜欢在(对象,对象)
选项。
当然,没有任何的 的转换>动作<串>
和动作<乌里>
,但为什么是相关的?如果我创造我自己的类 MyBaseClass
包含一个用户定义的 ==操作符(MyBaseClass X,MyBaseClass Y)
和我创建两个不相关的派生类,那么我的 ==
运营商仍然会使用(左,右操作数无法转换为对方,但都转换为 MyBaseClass
)。
只是为了完整性,这里是协方差类似的例子( Func键<出TResult>
),而不是逆变:
静态类Programf到
{
静态字符串TargetMethod()
{
返回假的;
}
静态无效的主要()
{
Func键<串GT;实例1 = TargetMethod;
Func键<串GT; INSTANCE2 = TargetMethod;
Func键< ICloneable> F1 = INSTANCE1;
Func键< IConvertible> F2 = INSTANCE2;
Console.WriteLine((对象)F1 ==(对象)F2);
Console.WriteLine((代表)F1 ==(代表)F2);
Console.WriteLine((Func键<串GT;)F1 ==(Func键<串GT;)F2);
Console.WriteLine(F1 F2 ==); //警告CS0253:可能无意引用比较;得到一个值比较,铸就右侧键入'System.Func< System.ICloneable>'
}
}
有关我的问题上面,的其中的在C#语言规范它说,这应是非法的一个问题:
Func键<串GT; G1 = ...;
Func键<&乌里GT; G2 = ...;
Console.WriteLine(G1 == G2); //错误CS0019:运算符'=='不能应用于类型的操作数'System.Func<串>'和'System.Func<的System.Uri>'
我可以看到,编译器想通了,没有类型都不能来自字符串继承
和乌里
(不像对 ICloneable
和 IConvertible
),所以这(如果它是合法的),只能成为真正
如果两个变量是空
,但如果它说,我不允许去做这个?在这种情况下,如果编译器选择了它不会有问题 ==操作符(对象,对象)
或 ==操作符(委托代表)
,因为,正如我所说,它归结为检查,如果都为null引用,这两个重载做同样的方式。
问:但是为什么第四的WriteLine导致的(对象,对象)重载使用
块引用>
由于它的编译器的唯一选择: - )
不能申请运营商'=='来操作数类型'System.Func< System.ICloneable>'和'System.Func< System.IConvertable>'
候选人是:
布尔==(System.Delegate,System.Delegate)
布尔==(系统.FUNC< System.ICloneable>中System.Func< System.ICloneable>)
布尔==(System.Func< System.IConvertable>中System.Func< System.IConvertable>)
因此,使用你的(对象,对象),编译器发现一个不错的选择。
同为
不能申请运营商操作==到类型的操作数'System.Action<串>'和System.Action<的System.Uri>'
候选人是:
布尔==(System.Delegate,System.Delegate)
布尔==(System.Action<串>中System.Action<串GT; )
布尔==(System.Action<的System.Uri>中System.Action<&的System.Uri GT;)
What are the precise rules for overload resolution with
==
between two expressions of delegate type?Consider the following code (where
using System;
is needed):static class ProgramA { static void TargetMethod(object obj) { } static void Main() { Action<object> instance1 = TargetMethod; Action<object> instance2 = TargetMethod; Action<string> a1 = instance1; Action<Uri> a2 = instance2; Console.WriteLine((object)a1 == (object)a2); Console.WriteLine((Delegate)a1 == (Delegate)a2); Console.WriteLine((Action<object>)a1 == (Action<object>)a2); Console.WriteLine(a1 == a2); // warning CS0253: Possible unintended reference comparison; to get a value comparison, cast the right hand side to type 'System.Action<string>' } }
Explanation:
instance1
andinstance2
are two separate instances of the same run-time type, the genericAction<in T>
which is contravariant inT
. Those instances are distinct butEquals
since they the have same targets.
a1
anda2
are the same asinstance1
andinstance2
, but because of the contravariance ofAction<in T>
there exist implicit reference conversions fromAction<object>
to each ofAction<string>
andAction<System.Uri>
.Now, the C# Language Specification has (among other overloads) these
operator ==
:bool operator ==(object x, object y); // §7.10.6 bool operator ==(System.Delegate x, System.Delegate y); // §7.10.8
The current Visual C# compiler realizes the first one by simply checking if the references are the same (the IL does not actually call a mscorlib method like
object.ReferenceEquals
, but that would give the same result), while it realizes the second one by callingDelegate.op_Equality
method which looks like a "user-defined" operator inside that assembly even when it is defined by the C# Language Spec, so is maybe not "user-defined" in the sense of the spec(?).Note that §7.10.8 is a little confusing because it says "Every delegate type implicitly provides the following predefined comparison operator[s]" and then gives the operator with the
(System.Delegate, System.Delegate)
signature. That is just one operator, not one for "every" delegate type? This seems important for my question.It is not surprising that the three first
WriteLine
writeFalse
,True
andTrue
, respectively, given what I said above.Question: But why does the fourth
WriteLine
lead to the(object, object)
overload being used?There does exist an implicit reference conversion from
Action<>
(or any other delegate type) toSystem.Delegate
, so why can't that be used here? Overload resolution should prefer that over the(object, object)
option.Of course, there are no implicit conversions between
Action<string>
andAction<Uri>
, but why is that relevant? If I create my own classMyBaseClass
containing a user-definedoperator ==(MyBaseClass x, MyBaseClass y)
and I create two unrelated deriving classes, then my==
operator will still be used (left and right operand not convertible to each other but both convertible toMyBaseClass
).
Just for completeness, here is the analogous example with covariance (
Func<out TResult>
) instead of contravariance:static class ProgramF { static string TargetMethod() { return "dummy"; } static void Main() { Func<string> instance1 = TargetMethod; Func<string> instance2 = TargetMethod; Func<ICloneable> f1 = instance1; Func<IConvertible> f2 = instance2; Console.WriteLine((object)f1 == (object)f2); Console.WriteLine((Delegate)f1 == (Delegate)f2); Console.WriteLine((Func<string>)f1 == (Func<string>)f2); Console.WriteLine(f1 == f2); // warning CS0253: Possible unintended reference comparison; to get a value comparison, cast the right hand side to type 'System.Func<System.ICloneable>' } }
A question related to my question above is, where in the C# Language Specification does it say that this shall be illegal:
Func<string> g1 = ...; Func<Uri> g2 = ...; Console.WriteLine(g1 == g2); // error CS0019: Operator '==' cannot be applied to operands of type 'System.Func<string>' and 'System.Func<System.Uri>'
I can see that the compiler figured out that no type can ever inherit from both
string
andUri
(unlike the pairICloneable
andIConvertible
), and so this (if it were legal) could only becometrue
if both variables werenull
, but where does it say that I am not allowed to do this? In this case it would not matter if the compiler choseoperator ==(object, object)
oroperator ==(Delegate, Delegate)
since, as I said, it comes down to checking if both are null references, and both overloads do that in the same way.解决方案Question: But why does the fourth WriteLine lead to the (object, object) overload being used?
Because it's the only choice for the compiler :-)
Cannot apply operator '==' to operands of type 'System.Func<System.ICloneable>' and 'System.Func<System.IConvertable>'
candidates are:
bool==(System.Delegate, System.Delegate) bool==(System.Func<System.ICloneable>, System.Func<System.ICloneable>) bool==(System.Func<System.IConvertable>, System.Func<System.IConvertable>)
so using your (object, object) is the best choice the compiler finds.
same for the Actions
Cannot apply operator '==' to operands of type 'System.Action<string>' and 'System.Action<System.Uri>'
candidates are:
bool==(System.Delegate, System.Delegate) bool==(System.Action<string>, System.Action<string>) bool==(System.Action<System.Uri>, System.Action<System.Uri>)
这篇关于在==操作符重载解析与变异泛型委托类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!