当“相同”的用户定义转换存在两次时,没有编译错误

时间:2011-02-22 23:53:19

标签: c# compiler-errors user-defined

在C#中,为什么在“相同”的用户定义转换存在两次时没有编译错误? (一次在源类中,一次在目标类中?)

例如,如果我尝试编译以下代码,则不会出现编译错误:

namespace TestConversionOverloading
{
    public class A
    {
        public int value = 1;

        public static explicit operator B(A a)
        {
            B b = new B();

            b.value = a.value + 6;

            return b;
        }
    }

    public class B
    {
        public int value = 2;

        public static explicit operator B(A a)
        {
            B newB = new B();

            newB.value = a.value;

            return newB;
        }
    }

    public class program
    {
        public static void Main() {}
    }
}

但是,如果我尝试将A显式转换为B,那么 do 会出现编译错误。假设我将以下内容添加到Main()并尝试编译:

A a = new A();
B b = ((B)a);

我会得到以下内容:

  

模糊的用户定义转化次数   'TestConversionOverloading.A.explicit operator TestConversionOverloading.B(TestConversionOverloading.A)'
     “TestConversionOverloading.B.explicit   操作者   TestConversionOverloading.B(TestConversionOverloading.A)'
  转换为'TestConversionOverloading.A''TestConversionOverloading.B'

那么为什么不直接从定义中给出错误?有没有办法使用这两种转换?

4 个答案:

答案 0 :(得分:3)

我不会推测为什么语言允许这样做是有意义的,但是如果你控制这两个类,那么显而易见的解决办法是摆脱其中一个运算符。

如果你不能,这是一种消除反思歧义的方法。

首先,创建一个绑定到目标运算符的委托:

// Picks the conversion operator declared in class A.
var method = typeof(A).GetMethod("op_Explicit", new[] { typeof(A) });
var converter = (Func<A, B>)Delegate.CreateDelegate(typeof(Func<A, B>), method);

然后使用委托:

A a = ...
B b = converter(a);

答案 1 :(得分:2)

根据the spec,这是预期的行为。

严重压缩原始文本,这是在这种情况下发生的情况:编译器将找到所有可以在两个类定义中将A转换为B 的运算符。这将招募A operator B(A a)B operator B(A a)。然后,

  

如果不存在此类运算符,或者如果存在多个此类运算符,则转换不明确并且发生编译时错误。

那么为什么不直接从定义中给出错误?因为这两个定义都没问题,但是它们的使用会导致问题出现。

有没有办法可以使用这两种转换?我没有看到一种简单的方法来执行此操作。我正在考虑绕过编译器,手动发射IL。这样我认为你可以指示程序使用一个操作符或另一个操作符。但不确定这是否完全可行。像Reflector这样的工具可以提供帮助。

虽然使用基于运算符的转换有一些美感,但其中一个类会丢失一个运算符,或者您可以更改为基于构造函数的转换或更简单的ToA(A a)FromA(A a)语法。或许Eric Lippert可以用一些语言聪明来启发我们!

答案 2 :(得分:1)

查看根据“公共静态隐式运算符B(A a)”代码行生成的IL代码:

.method public hidebysig specialname static
class TestConversionOverloading.B  op_Explicit(class TestConversionOverloading.A a) cil managed

所以这是第一个问题的答案:隐式/显式转换运算符是语法糖。在MSIL中,它们看起来像通常的方法(它们是)。当两个不同的类具有相同签名的方法时没有任何罪行,因为它没有违反任何内容。虽然在这种情况下无法编译转换运算符调用。 正如提到的那样,您可以使用反射来获取任一方法的MethodInfo。

答案 3 :(得分:0)

请记住,其中一个冲突的转化可能是通用的,并且可能对其他通用参数组合有用。

您甚至可以在SAME类中定义冲突转换:

class C<T>
{
    implicit operator int() { return 0; }
    implicit operator T() { return default(T); }
}

C<int> c;
int i = c;

如果编译器抱怨这一点,您将失去C<string>转换为stringint的能力。