CS0121 - 跨越Roslyn,CSC v12.0和单声道编译器的不一致的重载决策

时间:2016-05-27 15:19:32

标签: c# mono roslyn

在将Xamarin项目上的代码拉入运行Visual Studio 2015.2的Win 10盒子中的Xamarin项目中,我已经注意到奇怪的编译器特定的重载决策执行。我的好奇心最终导致我this post,这得益于Jon Skeet和Eric Lippert的关注,并且在这个问题上提供了很多信息。

那就是说,我想要看看这两个编译器之间的差异最小复制故事是什么,我设法做的是创建以下两个片段。

我将从Roslyn的成功案例开始:     

using System;

public static class Program
{
    public static void Main() { Foo(Bar); } // Outputs Func

    public static void Bar(string input) { Console.WriteLine(input); }
    public static string Bar() { return string.Empty; }

    public static void Foo(Action<string> input) { input("Action"); }
    public static void Foo(Func<string> input) { Console.WriteLine("Func"); }
}

Mono的工作原理:

using System;

public static class Program
{
    public static void Main() { Foo(Bar); }

    public static void Bar() { Console.WriteLine("Action"); }

    public static void Foo(Action input) { input(); }
    public static void Foo(Func<string> input) { Console.WriteLine("Func"); }
}

最后回顾使用的编译器版本:

罗斯林:1.2.0.60425

v12:12.0.31010.0

mono:4.2.3.0

所以我现在已经超出了我的深度,我的理解是规范对事情的处理方式有点模糊。拥有特定于编译器有效的代码显然不是最好的,因此我可能只需要请求团队成员提供lambda表达式以避免对方法组的歧义,或者至少是显式的强制转换。

Roslyn案对我来说特别奇怪,因为看起来它的决定是最随意的选择。任何额外的见解将不胜感激。

编辑:

我确实找到了一个可以在Mono和Roslyn上成功编译但在v12编译器上失败的附加代码片段:

using System;

public static class Program
{
    public static void Main() { Foo(Bar); }

    public static string Bar() { return string.Empty; }

    public static void Foo(Action bar) { Console.WriteLine("BAR"); }
    public static void Foo(Func<string> input) { Console.WriteLine("Func"); }
}

1 个答案:

答案 0 :(得分:2)

好吧,我离开了这一段时间,做了一些其他事情,简化了一些用法,我认为我已经确定了以下内容,并帮助找到了this in the specifications.

  

7.5.3.5更好的转化目标

     

鉴于T1和T2两种不同的类型,T1是更好的转换目标   如果

,则为T2      
      
  • 存在从T1到T2的隐式转换

  •   
  • T1是委托类型D1或表达式树类型   表达式T2是委托类型D2或表达式树   类型表达式,D1具有返回类型S1和以下之一   成立:

         
        
    • D2无效返回

    •   
    • D2的返回类型为S2,S1是比S2更好的转换目标

    •   
  •   

这里的问题似乎是,如果组中竞争方法之间的参数数量相同,则非void返回类型将始终是首选。因此,从前面提到的Eric Lippert's example来看,曾经是编译器错误的东西现在是可以接受的,假设它是一个完全匹配预期的传入方法组的单个非void返回类型。 但是,奇怪的是,即使组中的void方法是目标方法接受参数的唯一逻辑匹配,返回类型的方法也是首选,无法编译。 < / p>

所以方法组似乎确实考虑了Roslyn下选择的返回类型,只要给定相等的输入参数,方法组中只有一个可能的成员具有除void之外的返回类型

//Fine in Roslyn, not so much in Mono
using System;

public static class Program
{
    public static void Main() { Foo(Bar); } // Outputs Func

    public static string Bar() { return string.Empty; }
    public static void Bar(string s1, string s2, string s3) { }
    public static void Bar(string s1, string s2) {}
    public static void Bar(string s1) { }

    //untick for compile failure
    //public static void Foo(Func<int> input) { }
    public static void Foo(Action<string, string, string> input) { }
    public static void Foo(Action<string, string> input) { }
    public static void Foo(Action<string> input) { }
    public static void Foo(Func<string> input) { Console.WriteLine("Func"); }
}

虽然Mono似乎走向了相反的方向,而是将返回类型视为选择过程的一部分,这似乎意味着§7.6.5.1的单声道实现与Roslyns不同。

//Fine in mono, not so much in Rosyln
using System;

public static class Program
{
    public static void Main() { Foo(Bar); }  // Outputs Func<string>

    public static string Bar(string s1, string s2, string s3) { return "Fail"; }
    public static string Bar(string s1, string s2) { return "Fail"; }
    public static string Bar(string s1) { return "Fail"; }
    public static string Bar() { return "Pass"; }

    // untick for compile failure
    // public static void Foo(Func<string,string,string,string> input) { Console.WriteLine("<string,string,string,string>"); }
    public static void Foo(Func<string> input) { Console.WriteLine("Func<string>"); }
    public static void Foo(Func<int> input) { Console.WriteLine("Func<int>"); }
    public static void Foo(Func<decimal> input) { Console.WriteLine("Func<decimal>"); }
    public static void Foo(Func<char> input) {Console.WriteLine("Func<char>");}
}

由于Eric Lippert之前概述的原因,两者都不适用于之前的编译器版本。至少,这对我来说是有益的,虽然我真的不知道我学到了什么。我想不要重载方法组,如果你这样做,请确保它对两个编译器都很好。