[英]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());
}
}
为什么会有所不同?
因为类型推断推断T
是Foo
,因此我们将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.