CSharp - C# 等价隐式运算符:为什么它们是合法的?

  显示原文与译文双语对照的内容
80 5

更新 !

我看到我对下面的C# 规范的剖析,我认为我一定丢失了一些东西,因为我在这个问题中描述的行为实际上违背了规范的规范。

更新 2 !

好的,在进一步的反思之后,根据一些评论,我想现在我已经知道了。 spec规范中的"源类型"refer --下面的例子,这就是编译器可以将候选对象缩小到定义的两个操作符的narrow 。 然而,它不能进一步缩小选择范围。 spec ( 因为它适用于这个问题) 中的关键字是 "源类型",我以前误解了( 我觉得),这意味着"声明类型。"

原始问题

假设我定义了以下类型:


class Type0


{


 public string Value { get; private set; }



 public Type0(string value)


 {


 Value = value;


 }


}



class Type1 : Type0


{


 public Type1(string value) : base(value) { }



 public static implicit operator Type1(Type2 other)


 {


 return new Type1("Converted using Type1's operator.");


 }


}



class Type2 : Type0


{


 public Type2(string value) : base(value) { }



 public static implicit operator Type1(Type2 other)


 {


 return new Type1("Converted using Type2's operator.");


 }


}



然后说我这么做:


Type2 t2 = new Type2("B");


Type1 t1 = t2;



显然这是不明确的,因为不清楚应该使用哪个 implicit 操作符。 因为i的方法是resolve的,所以上面的类定义编译了 -- ,所以编译器在所有的all中都允许这些匹配的implicit 运算符

剖析

好,我将通过摘录 C# Passant引用的规范来尝试解决这一问题。

查找用户定义的转换运算符将从中考虑的类型的集合。 这个集合由( 如果0 是类或者结构) 。( 如果S 是类)的基类和T ( 如果T 是类或者结构) 组成。

我们正在从 Type2 ( ) 到 Type1 ( 英镑) 转换。 因此,这里看来,在这里示例中,包含所有三种类型: Type0 ( 因为它是的基础类别),Type1 ( 英镑) 和 Type2 ( ) 。

查找适用的用户定义转换运算符集,例如。 集由由D 中的类或者结构声明的用户定义的隐式转换运算符组成,由包含S 类型的类型组成。 如果为空,则转换未定义并且发生编译时错误。

好的我们有两个操作符满足这些条件。 由于 Type1 在,并且它从 Type2 ( 这显然包含了 ) 转换为 Type1 ( 这显然包含了英镑),所以在 Type1 中声明的版本符合需求。Type2 中的版本同样满足同样的原因。 因此,英镑包括这两个运算符。

最后,关于寻找最具体的"源类型",以英镑为单位的运营商 SX:

如果从S 转换的任一运算符为 ,则SX为S 。

现在,operators两个 operators convert convert convert convert convert,这告诉我 SX SX is is 。

英镑并不意味着应该使用版本?

等等我被搞糊涂了 !

如果只有定义的Type1 版本,那么只有 Type1 版本,而且按照规格收费,最终的候选版本是1.? 这似乎是一个可以能的场景,规范授权某些不可以能的( 也就是说,在 Type2 中声明的转换应该在实际上不存在的情况下使用) 。

时间: 原作者:

116 3

我们并不真正希望它是编译时错误,只是为了定义转换,因为它可能会导致歧义。 假设我们改变Type0来存储 double,由于某些原因,我们希望为有符号整数和无符号整数提供单独的转换。


class Type0


{


 public double Value { get; private set; }



 public Type0(double value)


 {


 Value = value;


 }



 public static implicit operator Int32(Type0 other)


 {


 return (Int32)other.Value;


 }



 public static implicit operator UInt32(Type0 other)


 {


 return (UInt32)Math.Abs(other.Value);


 }



}



这将编译良好,而且我可以使用两种转换和


Type0 t = new Type0(0.9);


int i = t;


UInt32 u = t;



然而,它是一个编译错误 float f = t 因为任何隐式转换都可以用于获取一个整数类型,然后可以转换为浮点类型。

我们只希望编译器在实际使用时对这些更复杂的歧义抱怨,因为我们希望上面的Type0编译。 为了一致性,更简单的模糊性也应该在你使用它的时候引起错误,而不是定义。

编辑

由于:删除了引用规范的回答,下面是确定转换是否明确的C# 规范的一部分

  • 查找美国运营商最具体的源类型 SX:
    • 如果从S 转换的任何一个操作符,那么SX就是。
    • 否则,SX是组合的目标类型组合集合中最包含的类型。 如果找不到最包含的类型,则转换是不明确的,并且发生编译时错误。

我们更喜欢转换直接从S 转换,否则我们更喜欢转换为"最容易"的类型。 在这两个示例中,我们有两个。 如果没有 Type2的转换,我们宁愿从 Type0 转换到 object的转换。 如果没有一种类型显然是更好的选择,那么我们就在这里。

  • 查找美国运营商中最具体的目标类型 TX:
    • 如果转换为T的任何运算符,则TX为。
    • 否则,TX是组合的目标类型组合集合中最具包容性的类型。 如果找不到最接近的类型,那么转换是不明确的,编译时出错。

同样,我们更愿意直接转换为T,但是我们将为"最容易"转换为T的类型。 在dan的例子中,我们有两个转换可用。 在我的示例中,可以能的目标是 Int32UInt32,也不是比另一个更好的匹配,因这里转换失败。 编译器无法知道是否 float f = t 意味着 float f = (float)(Int32)t 或者 float f = (float)(UInt32)t

  • 如果你只包含一个由SX转换为TX的用户定义转换操作符,那么这是最具体的转换操作符。 如果不存在此类运算符,或者存在多个这样的运算符,则转换不明确,并且发生编译时错误。

在dan的例子中,我们失败了,因为我们从SX到TX还有两个转换。 如果选择rtc和 TX,我们可以在选择不同的转换时将when转换为 TX 。 比如,如果我们有从 Type1 派生的Type1a,那么我们可能会从 Type2 转换为 Type1a,从 Type0Type1,这仍然提供了SX=Type2和 TX=Type1,但是我们实际上没有从Type2到Type1的任何转换。 这是可以的,因为这是不明确的。 编译器不知道是否将Type2转换为 Type1a,然后转换为 Type1,或者先转换为 Type0,以便它可以使用转换为 Type1.

143 3

最终,它不能是完全成功的prohibitted 。 你和我可以发布两个程序集。 他们可以开始使用它的他组装,而更新我们自己的。 然后我们可以在每个程序集定义的类型之间提供隐式转换。 只有当我们发布下一个版本时,这个版本才会被捕获,而不是编译时。

不试图禁止禁止被禁止的事情有一个优势,因为它使得清晰和一致性变得更加清晰。

原作者:
...