【问题标题】:CS0121 - Inconsistant overload resolution across Roslyn, CSC v12.0, and the mono compilersCS0121 - 跨 Roslyn、CSC v12.0 和单声道编译器的重载解决方案不一致
【发布时间】:2016-05-27 15:19:32
【问题描述】:

在将 Xamarin 项目上的代码提取到运行 Visual Studio 2015.2 的 Win 10 机器中后,我注意到奇怪的编译器特定强制重载解析。我的好奇心最终将我引向了this post,这得到了 Jon Skeet 和 Eric Lippert 的关注,并且在这个主题上提供了非常丰富的信息。

也就是说,我想看看这两个编译器之间的这种差异的最小复制故事是什么,我设法做的是创建以下两个 sn-ps。

我将从 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

单声道:4.2.3.0

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

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

编辑:

我确实设法找到了一个额外的 sn-p,它可以在 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"); }
}

【问题讨论】:

  • 你用的是什么版本的单声道?
  • 4.5 x86,mcs 显示 4.0.30319.17020
  • 这不是 Mono 版本,而是配置文件版本 (4.5) 和运行时版本 (4.0.30319....)。你必须通过mono --version查看
  • 那只是报告抖动的版本,即4.2.3
  • 是的,但是在大多数情况下,Mono 想要遵循 MS.NET 所做的事情,即使后者有点错误或者它的行为超出规范,所以请将不同的行为归档为错误在bugzilla.xamarin.com(现在 xamarin 是 MS 的一部分,他们实际上可能会在 roslyn 中修复它,但他们需要先知道)

标签: c# mono roslyn


【解决方案1】:

好的,我暂时离开了这个,做了一些其他的事情,简化了一些用法,我想我已经确定了以下内容,在找到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 方法是目标方法接受参数的唯一逻辑匹配,具有返回类型的方法将被首选并且无法编译。

所以方法组似乎确实考虑了在 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 的 mono 实现与 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 之前概述的原因,两者都不会在以前的编译器版本中工作。至少,这对我来说是有益的,虽然我真的不知道我学到了什么。我想不要重载方法组,如果这样做,请确保它与两个编译器都很好。

【讨论】:

    猜你喜欢
    • 2019-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-01
    • 1970-01-01
    相关资源
    最近更新 更多