繁体   English   中英

委托返回类型与lambda函数不同

[英]Delegate return type different with lambda function

考虑一下这个MCVE:

using System;

public interface IThing { }

public class Foo : IThing
{
    public static Foo Create() => new Foo();
}

public class Bar : IThing
{
    public static Bar Create() => new Bar();
}

public delegate IThing ThingCreator();

class Program
{
    static void Test(ThingCreator creator)
    {
        Console.WriteLine(creator.Method.ReturnType);
    }

    static void Main()
    {
        Test(Foo.Create);      // Prints: Foo
        Test(Bar.Create);      // Prints: Bar

        Test(() => new Foo()); // Prints: IThing
        Test(() => new Bar()); // Prints: IThing
    }
}

为什么反映静态工厂方法的返回类型给出具体类型,而调用构造函数内联给出接口? 我希望它们都是一样的。

另外,有没有办法在lambda版本中指定我希望返回值是具体类型? 或者调用静态方法是唯一的方法吗?

lambda表达式的返回类型不是从lambda acutally返回的内容推断出来的,而是从它所指定的类型推断出来的。 即,你不能像这样分配一个lambda(除非通用类型参数被调用;参见Eric Lippert的评论):

// This generates the compiler error:
// "Cannot assign lambda expression to an implicitly-typed variable".
var lambda = () => new Foo();

你必须总是做这样的事情(lambdas总是分配给委托类型):

Func<MyType> lambda = () => new Foo();

因此在Test(() => new Foo()); 拉姆达的返回类型是从它被分配给(参数的类型确定IThing ,的返回类型ThingCreator )。

Test(Foo.Create); 你根本没有lambda,但是声明为public static Foo Create() ... 这里明确指定了类型,并且是Foo (无论是静态方法还是实例方法都没有区别)。

Olivier的答案基本上是正确的,但它可以使用一些额外的解释。

为什么反映静态工厂方法的返回类型给出具体类型,而调用构造函数内联给出接口? 我希望它们都是一样的

您的Program类等同于以下类:

class Program
{
  static void Test(ThingCreator creator)
  {
    Console.WriteLine(creator.Method.ReturnType);
  }
  static IThing Anon1() 
  {
    return new Foo();
  }
  static IThing Anon2()
  {
    return new Bar();
  }
  static void Main()
  {
    Test(new ThingCreator(Foo.Create));
    Test(new ThingCreator(Bar.Create));
    Test(new ThingCreator(Program.Anon1));
    Test(new ThingCreator(Program.Anon2));
  }
}

现在应该清楚为什么程序会打印它的功能。

这里的故事的寓意是,当我们为lambda生成隐藏方法时,那些隐藏方法返回委托所需的 任何内容 ,而不是lambda返回的内容

这是为什么?

一个更密切关注的例子将证明:

static void Blah(Func<object> f) 
{ 
  Console.WriteLine(f().ToString());
}
static void Main()
{
  Blah( () => 123 );
}

我希望你同意这必须生成为

static object Anon() { return (object)123; }

并不是

static int Anon() { return 123; }

因为后者无法转换为Func<object> 拳击指令无处可去,但对ToString的调用需要f()返回引用类型。

因此,一般规则是lambda在被称为隐藏方法时,必须具有通过转换为委托类型而授予它的返回类型。

另外,有没有办法在lambda版本中指定我希望返回值是具体类型? 或者调用静态方法是唯一的方法吗?

当然。

interface IThing {}
class Foo : IThing {}
delegate T ThingCreator<T>() where T : IThing;
public class Program
{
  static  void Test<T>(ThingCreator<T> tc) where T : IThing
  {
    Console.WriteLine(tc.Method.ReturnType);
  }
  public static void Main()
  {
    Test(() => new Foo());      
  }
}

为什么会有所不同?

因为类型推断推断TFoo ,因此我们将lambda转换为ThingCreator<Foo> ,它具有返回类型Foo 因此,生成的方法返回Foo ,如委托类型所期望的那样。

但是等等,你说......我不能改变Test或ThingCreator的签名

别担心! 你仍然可以做这个工作:

delegate T ThingCreator<T>() where T : IThing;
delegate IThing ThingCreator();    
public class Program
{
    static void Test(ThingCreator tc)
    {
      Console.WriteLine(tc.Method.ReturnType);
    }
    static ThingCreator DoIt<T>(ThingCreator<T> tc) where T : class, IThing
    { 
      return tc.Invoke; 
    }
    public static void Main()
    {
      Test(DoIt(() => new Foo()));
    }
}

ThingCreator是,现在每个非通用的ThingCreator委托都是一个调用ThingCreator<T>委托的委托,这有点浪费时间和内存。 但是,您可以获得类型推断,方法组转换和lambda转换,以使您获得所需返回类型的方法,以及该方法的委托。

注意class约束。 你明白为什么那个约束必须存在吗? 这留给读者练习。

我的个人猜测是调用位置。 当你将() => new Foo()传递给函数时,它将它作为ThingCreator并调用它来获取IThing 但是,当测试方法调用它时,将具体类型的工厂方法发送到Test方法时,它将转到具体类型的Create() ,然后返回具体对象,这是完全可以接受的,因为它也是IThing

我想你需要的不仅仅是我的猜测。 对不起,如果我错了!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM