C# 为什么可以';编译器没有发现这个lambda是一个委托吗?

C# 为什么可以';编译器没有发现这个lambda是一个委托吗?,c#,.net,lambda,delegates,C#,.net,Lambda,Delegates,考虑一下这个小程序 public class Program { public static void Main() { // the first path compiles RunAction(() => { }); // the second path does not compile RunDelegate(() => {}); } private static void Run

考虑一下这个小程序

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) { }
}
第一条路径编译并暗示

  • ()=>{}
    lambda是一个
    操作
  • 操作
    是一个
    委托
    ,并且
  • 因此,
    ()=>{}
    lambda是
    委托
  • 为什么第二条路径不编译?

    通常,编译器能够在步骤(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 { }
    
    匿名函数(如
    ()=>{}
    )没有“默认”委托类型。这样的表达式可以隐式转换为任何具体的委托类型,即右签名和返回类型(根据各种自然变化规则)

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

    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)(() => { }));
    ...
    
    对你的问题的评论是正确的

    与此相关的事实是,这不会编译:

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

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

    不幸的是,当您将
    委托
    作为一种类型处理时,编译器必须推断出一种实际的委托类型以用于委托,并且可能有许多可能的委托类型可用于实际的代码。编译器只是简单地说“不,不能那样做”,因为这是语言设计者在本例中决定要做的。假设(1)是不正确的
    ()=>{}
    不是一个
    操作
    ,它在代码中的某个地方没有类型声明
    操作
    ,允许编译器进行推理。@spender Right。我现在回忆起
    Action run=()=>{}
    运行操作(()=>{})
    都将lambda声明为
    操作
    。在第二种情况下,这就不那么直观了。具体来说,问题不是编译器无法确定您正在处理的是
    委托
    ,而是编译器不理解您正在处理的是哪个特定委托,它们都是从
    Delegate
    派生的实例。一个有趣的决定是
    var x=new{}
    适用于匿名对象,但
    var y=()=>{}
    不适用于lambdas。令我惊讶的是,编译器没有发现后者是一个
    操作
    。另一个不对称性是,我们可以强制转换lambda,但不能强制转换匿名对象。