更新!
请参阅下面我对 C# 规范的一部分的剖析;我想我一定遗漏了一些东西,因为在我看来,我在这个问题中描述的行为实际上违反了规范。
更新2!
好的,经过进一步思考,并根据一些评论,我想我现在明白发生了什么。规范中的“源类型”一词指的是转换自的类型——即,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
应该使用哪个运算符。我的问题是——因为我看不到任何解决这种歧义的方法(它不像我可以执行一些显式转换来澄清我想要的版本),但是上面的类定义确实编译——为什么编译器允许那些匹配的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 为空, 转换未定义并发生编译时错误。
好的,我们有两个满足这些条件的运算符。in 声明的版本Type1
符合要求,因为Type1
它在D中,并且它从Type2
(显然包含S)转换为Type1
(显然包含在T中)。出于完全相同的原因,in 中的版本Type2
也符合要求。所以U包括这两个运算符。
最后,关于在U中查找运算符的最具体的“源类型” SX:
如果 U 中的任何运算符从 S 转换,则 SX 是 S。
现在,U中的两个运算符都从S转换- 所以这告诉我SX是S。
这不是意味着Type2
应该使用该版本吗?
可是等等!我很困惑!
难道我不能只定义Type1
' 运算符的版本,在这种情况下,唯一剩下的候选者将是Type1
' 版本,但根据规范SX将是Type2
?这似乎是一种可能的情况,其中规范要求一些不可能的事情(即,Type2
应该使用中声明的转换,而实际上它不存在)。