【发布时间】:2010-08-24 22:48:56
【问题描述】:
更新!
请参阅下面我对 C# 规范的一部分的剖析;我想我一定遗漏了什么,因为对我来说,我在这个问题中描述的行为似乎实际上违反了规范。
更新 2!
好的,经过进一步思考,并基于一些 cmets,我想我现在明白发生了什么。规范中的“源类型”一词指的是要转换的类型 from ——即,在我下面的示例中,Type2 ——这仅仅意味着编译器能够将候选者缩小到定义的两个运算符(因为Type2 是两者的源类型)。但是,它不能进一步缩小选择范围。所以规范中的关键词(因为它适用于这个问题)是“源类型”,我之前误解(我认为)意思是“声明类型”。
原始问题
假设我定义了这些类型:
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 运算符。我的问题是——因为我看不到解决这种歧义的 any 方法(我不能执行一些明确的强制转换来澄清我想要的版本),但是上面的类定义确实可以编译-- 为什么编译器会允许那些匹配的implicit 运算符?
解剖
好的,我将逐步阅读 Hans Passant 引用的 C# 规范的摘录,以试图理解这一点。
找到类型集 D,从中 用户定义的转换运算符将 被考虑。这组包括 S (如果 S 是一个类或结构),基 S 的类(如果 S 是一个类),以及 T (如果 T 是一个类或结构)。
我们将从 Type2 (S) 转换为 Type1 (T)。所以这里的 D 似乎会包含示例中的所有三种类型:Type0(因为它是 S 的基类)、Type1( T) 和Type2 (S)。
找到适用的集合 用户定义的转换运算符,U。 该集合包括用户定义的 声明的隐式转换运算符 通过 D 中的类或结构 从包含 S 的类型转换为 T 包含的类型。如果 U 是 为空,转换未定义并且 发生编译时错误。
好的,我们有两个满足这些条件的运算符。 Type1 中声明的版本符合要求,因为 Type1 位于 D 中,它从 Type2(显然包含 S)转换为 Type1(其中显然被 T 包含)。 Type2 中的版本 也 出于完全相同的原因满足要求。所以 U 包括这两个运算符。
最后,关于在 U 中找到最具体的“源类型”SX 运算符:
如果 U 中的任何运算符从 S 转换,则 SX 是 S。
现在,U 中的 both 运算符从 S 转换——所以这告诉我 SX 是 S。
不是说要使用Type2的版本吗?
但是等等!我很困惑!
难道我不能仅定义Type1 的运算符版本,在这种情况下,唯一剩下的候选者将是Type1 的版本,但根据规范SX 会是Type2?这似乎是一种可能的情况,其中规范要求一些不可能的事情(即,在 Type2 中声明的转换实际上不存在时应该使用它)。
【问题讨论】:
-
一个声明可能优先于另一个。编译器选择哪一个?
-
@dtb:它允许类定义,但不允许底部的示例代码。它指出存在模棱两可的用户定义转换。换句话说,它允许代码编译,但绝不允许它被使用。
-
我会指出,如果有多个用户定义的显式转换,它会做同样的事情。
-
@mquander:嗯,至少它是一致的,那么 ;)
-
它可能不会检查,因为(a)如果两个类位于不同的命名空间中会很好,并且(b)它不知道冲突的声明是否在同一个命名空间中但在不同的命名空间中部件。所以当你声明它时,它会检查你何时使用它。
标签: c# .net implicit ambiguity implicit-conversion