当派生类型为1作为参数传递时,双向隐式可转换类型的重载之间的模糊调用

GDS*_*GDS 6 c# overloading ambiguous implicit-cast

(试图找到一个总结问题的标题可能是一项非常艰巨的任务!)

我有以下类与一些重载方法,产生一个调用歧义编译器错误:

public class MyClass
{
    public static void OverloadedMethod(MyClass l) { }
    public static void OverloadedMethod(MyCastableClass l) { }

    //Try commenting this out separately from the next implicit operator. 
    //Comment out the resulting offending casts in Test() as well.
    public static implicit operator MyCastableClass(MyClass l)
    {
        return new MyCastableClass();
    }

    //Try commenting this out separately from the previous implicit operator.
    //Comment out the resulting offending casts in Test() as well.
    public static implicit operator MyClass(MyCastableClass l)
    {
        return new MyClass();
    }

    static void Test()
    {
        MyDerivedClass derived = new MyDerivedClass();
        MyClass class1 = new MyClass();
        MyClass class2 = new MyDerivedClass();
        MyClass class3 = new MyCastableClass();
        MyCastableClass castableClass1 = new MyCastableClass();
        MyCastableClass castableClass2 = new MyClass();
        MyCastableClass castableClass3 = new MyDerivedClass();

        OverloadedMethod(derived); //Ambiguous call between OverloadedMethod(MyClass l) and OverloadedMethod(MyCastableClass l)
        OverloadedMethod(class1);
        OverloadedMethod(class2);
        OverloadedMethod(class3);
        OverloadedMethod(castableClass1);
        OverloadedMethod(castableClass2);
        OverloadedMethod(castableClass3);

    }

public class MyDerivedClass : MyClass {  }

public class MyCastableClass { }
Run Code Online (Sandbox Code Playgroud)

有两个非常有趣的事情需要注意:

  1. 注释掉任何隐式运算符方法可消除歧义.
  2. 尝试重命名VS中的第一个方法重载将重命名Test()方法中的前四个调用!

这自然会带来两个问题:

  1. 什么是编译器错误背后的逻辑(即编译器如何达到歧义)?
  2. 这个设计有什么问题吗?直观地说,应该没有歧义,并且应该在第一个方法overload(OverloadedMethod(MyClass l, MyClass r))中解决违规调用,因为MyDerivedClass它与MyClasscastable 更密切相关,但在其他方面无关紧要MyCastableClass.VS重构似乎也同意这种直觉.

编辑: 在玩了VS重构之后,我看到VS将违规方法调用与代码中定义的第一个重载相匹配.因此,如果我们交换两个重载VS,则将违规调用与具有MyCastableClass参数的一个进行匹配.这些问题仍然有效.

Eri*_*ert 8

什么是编译器错误背后的逻辑(即编译器如何达到歧义)?

首先,我们必须确定方法组中的方法.显然,方法组中有两种方法.

其次,我们必须确定这两种方法中哪一种适用.也就是说,每个参数都可以隐式转换为相应的参数类型.显然,这两种方法都适用.

第三,鉴于存在多种适用方法,必须确定唯一的最佳方法.在只有两个方法,每个只有一个参数的情况下,规则是从参数参数类型转换必须优于另一个.

关于什么使一次转换比另一种转换更好的规则在规范的7.5.3.5节中,为方便起见,我在这里引用:

给定从类型S转换为类型T1的转换C1,以及从类型S转换为类型T2的转换C2,如果存在以下至少一个,则C1是比C2更好的转换:

•存在从S到T1的身份转换,但不存在从S到T2的身份转换

•T1是比T2更好的转换目标

给定两种不同类型T1和T2,如果至少有以下一种情况,则T1是比T2更好的转换目标:

•存在从T1到T2的隐式转换,并且不存在从T2到T1的隐式转换

此规则的目的是确定哪种类型更具体.如果每个香蕉都是水果而不是每个水果都是香蕉,那么香蕉比水果更具特色.

•T1是带符号的整数类型,T2是无符号整数类型.

在列表中运行.是否有身份转换MyDerivedClass为?MyCastableClassMyClass?不是.是否存在隐式转换MyClass,MyCastableClass不是隐式转换?没有.没有理由认为任何一种类型都比另一种更具体.是整体类型?没有.

因此,没有任何依据可以确定一个人比另一个人更好,因此这是不明确的.

这个设计有什么问题吗?

问题回答了自己.你发现了其中一个问题.

直观地说,应该没有歧义,并且应该在第一个方法重载中解决违规调用,因为MyDerivedClass与MyClass关系更密切

虽然这对您来说可能很直观,但在这种情况下,规范并未区分用户定义的转换和任何其他隐式转换.但是我注意到,在一些罕见的情况下,你的干扰确实很重要 ; 有关详细信息,请参阅我关于链式用户定义转换的文章.(http://blogs.msdn.com/b/ericlippert/archive/2007/04/16/chained-user-defined-explicit-conversions-in-c.aspx)

  • @GDS:你现在问道德问题"产品不按我设计的方式工作;产品是错误的吗?" 好吧,只要产品不按照用户认为应该的方式工作,*在设计或用户教育过程中出现问题*.但产品就是这样,改变它将是一个危险的突破性变化,会产生比产生效益更多的麻烦. (4认同)
  • @Raffael:我在这个网站上评论的成千上万的问题和答案中,我的downvotes是一小部分,并且仅限于那些积极伤害互联网的问题和答案.您是否通过批评其他放弃业余时间帮助他人的选择来增加本网站的价值? (3认同)