【问题标题】:Why can't the compiler figure out that this lambda is a Delegate?为什么编译器不能确定这个 lambda 是一个委托?
【发布时间】:2018-05-16 21:47:11
【问题描述】:

考虑一下这个小程序。

public class Program
{
    public static void Main()
    {
        // the first path compiles
        RunAction(() => { });

        // the second path does not compile
        RunDelegate(() => {});
    }

    private static void RunAction(Action run) => RunDelegate(run);
    private static void RunDelegate(Delegate run) { }
}

第一个路径编译并暗示

  1. () => {} lambda 是一个Action
  2. ActionDelegate,并且
  3. 因此() => {} lambda 是Delegate

为什么第二个路径不编译?

通常编译器能够在步骤 (1) 和 (3) 之间进行跳跃。这是编译器通常如何处理这种嵌套的 is-a 关系的示例。

public class Program2
{
    public static void Main()
    {
        // both of these comple
        AcceptPerson(new Programmer());
        AcceptAnimal(new Programmer());
    }

    private static void AcceptPerson(Person p) => AcceptAnimal(p);
    private static void AcceptAnimal(Animal a) { }
}

public class Programmer : Person { }
public class Person : Animal { }
public class Animal { }

【问题讨论】:

  • 不幸的是,当您将 Delegate 作为一种类型处理时,编译器必须推断出一个 actual 委托类型以用于您的委托,并且可能有任何用于您的实际代码的可能委托类型的数量。编译器只是说“不,不能那样做”,因为这是语言设计者在这种情况下决定做的事情。
  • 假设 (1.) 不正确。 () => {} 不是 Action 没有 Action somewhere 在代码中允许编译器进行推断的类型声明。
  • @spender 对。我现在记得Action run = () => {};RunAction(() => {}); 都将lambda 声明为Action。在第二种情况下,这不太直观。
  • 具体来说,问题不在于编译器无法确定您正在处理Delegate,问题在于编译器不了解您正在处理的具体委托with,它们都是派生自 Delegate 的实例。

标签: c# .net lambda delegates


【解决方案1】:

() => { } 等匿名函数没有“默认”委托类型。这样的表达式可以隐式转换为 any 正确签名和返回类型的具体委托类型(受各种自然变化规则的约束)。

您可以有许多匹配的委托类型:

namespace TestFramework
{
  public delegate void TestDelegate();
}

namespace System
{
  public delegate void Action();
}

namespace System.Threading
{
  public delegate void ThreadStart();
}

...

在所有这些可能的具体委托类型中(每个都作为抽象 System.Delegate 作为其(间接)基类)没有首选的一个

从表达式() => { } 到每个表达式都有一个隐式转换

TestDelegate f = () => { };
Action g = () => { };
ThreadStart h = () => { };
...

由于隐式转换,您的调用 RunAction(() => { }); 有效。

但是对于RunDelegate(() => {});,不知道要使用什么具体的委托类型。您必须执行以下操作之一:

RunDelegate((TestDelegate)(() => { }));
RunDelegate((Action)(() => { }));
RunDelegate((ThreadStart)(() => { }));
...

你的问题的cmets是正确的。

与此相关的是它不会编译:

var f = () => { };  // illegal, shows that '() => { }' does NOT have type System.Action

你不能使用像myBool ? (() => { }) : null这样的子表达式,等等。

【讨论】:

  • var x = new {} 适用于匿名对象但 var y = () => {} 不适用于 lambda 是一个有趣的决定。令我惊讶的是,编译器并没有发现后者是Action
  • 另一个不对称是我们可以转换 lambdas 但不能转换匿名对象。
猜你喜欢
  • 1970-01-01
  • 2018-12-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-09
相关资源
最近更新 更多