【问题标题】:When did C# get this "SFINAE-like" overload resolution of Action<..> parameters, and what is it called?C# 什么时候得到 Action<..> 参数的这种“类似 SFINAE”的重载解析,它叫什么?
【发布时间】:2020-12-24 04:14:35
【问题描述】:

考虑以下代码,其中 C# 可以根据提供的隐式类型 Action 消除重载的歧义,其中 T 是基于 [互斥] 类型派生的 -动作代码的有效性。

我很惊喜,意识到我已经“不假思索地依赖这种方便的行为”有一段时间了,尽管它要求编译器检查 Action 中的代码以及有效的目标站点类型。通常,类型必须在在目标表达式中使用之前解析。

这种行为是什么时候引入的?这种类型推断的正确名称是什么?有哪些限制和已知的极端情况?哪些文档链接(或规范中的部分)涵盖了该行为?

static void WithAction(Action<string> s) {
    Console.WriteLine("string action");
    s("string");
}

static void WithAction(Action<int> i) {
    Console.WriteLine("int action");
    i(1);
}

void Main()
{
    // Compiler has to INFER that this is an Action<string>
    // where the target type must be in {Action<string>, Action<int>}
    // (int does not have a Length property)
    WithAction(s => Console.WriteLine(s.Length == 1));

    // Ditto, except compiler infers Action<int>.
    // (there is no string == int operator overload)
    WithAction(i => Console.WriteLine(i == 1));

    // compiler error
    // CS0121 The call is ambiguous between the following methods or properties:
    // 'UserQuery.WithAction(Action<string>)' and 'UserQuery.WithAction(Action<int>)'
    // (there is both int == int and string == string!)
    // WithAction(z => Console.WriteLine(z == z));
}

代码在 LINQPad 5 中运行并产生以下输出:

string action
False
int action
True

【问题讨论】:

  • Jon Skeet 写了一篇很棒的博文,可能会帮助您找到一些答案:codeblog.jonskeet.uk/2010/10/28/…
  • @DavidL 感谢您的链接。但是,它似乎没有涵盖这种情况。在上面,它没有在例如之间决定。 Action&lt;T&gt;(更具体)和Action,它在Action&lt;string&gt; 和Action&lt;int&gt; 之间做出决定,其中必须“尝试编译”作为目标类型的操作以查看它们是否可以 匹配一个 XOR 其他重载方法。我已经包含了非编译情况,当方法的主体 not 足以让编译器推断 Action 的参数是 string XOR @ 987654329@(因此 Action 必须是 Action&lt;string&gt; 或 Action&lt;int&gt;)。

标签: c# syntax overloading type-inference type-systems


【解决方案1】:

C# 规范在 Anonymous Function Conversions 中对此进行了介绍。

匿名函数包括旧 delegate (...) { ... } 语法的 lambda 表达式。

在该部分中,它说匿名函数 F 可以转换为委托类型 D 提供:

如果F 的主体是一个表达式,并且D 具有void 返回类型或F 是async 并且D 具有返回类型Task,那么当每个参数的F 被赋予D 中相应参数的类型,F 的主体是一个有效的表达式 (wrt Expressions),可以作为 statement_expression。

本质上,关键是转换仅在“F 的主体是有效的(语句)表达式”时才有效。如果s 是int,Console.WriteLine(s.Length == 1) 不是有效的语句表达式,所以没有转换。

当编译器在重载解析期间考虑适用的函数成员时,根本不考虑采用Action&lt;int&gt; 的重载,因为没有(隐式)转换为Action&lt;int&gt;。

从这适用于所有匿名函数的事实来看,此功能可能并不新鲜。它可能与delegate (...) { ... } 一样古老。

【讨论】:

  • 我“最近”才意识到发生了一些特别的事情。我不在乎说我使用 C# 多久了。 ^_^
猜你喜欢
  • 1970-01-01
  • 2013-03-25
  • 1970-01-01
  • 1970-01-01
  • 2011-10-31
  • 2011-04-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多