我知道您不能从方法返回匿名类型,但我想知道 Select 扩展方法如何返回匿名类型。它只是一个编译器技巧吗?
编辑
假设 L 是一个列表。这是如何运作的?
L.Select(s => new { Name = s })
返回类型是 IEnumerable<'a> 其中 'a = new {String Name}
我知道您不能从方法返回匿名类型,但我想知道 Select 扩展方法如何返回匿名类型。它只是一个编译器技巧吗?
编辑
假设 L 是一个列表。这是如何运作的?
L.Select(s => new { Name = s })
返回类型是 IEnumerable<'a> 其中 'a = new {String Name}
好吧,这是泛型方法类型参数的正常类型推断。例如:
List<string> x = new List<string>();
// The compiler converts this:
x.Select(y => y.Length);
// Into this, using type inference:
Enumerable.Select<string, int>(x, y => y.Length);
如果x
是某个匿名类型的列表,或者推断的 lambda 表达式的返回类型是匿名类型,情况也是如此。不要忘记,即使您不能明确说明使用匿名类型的变量的类型,它仍然具有编译器已知的确定类型。
类型实际上是由调用者定义的,所以它在调用函数的范围内——巧妙地避免了“返回”匿名类型的问题。
这是通过泛型类型推断完成的。Select的签名是Select<Tsource, TResult>(IEnumerable<TSource>, Func<TSource, TResult>
. IEnumerable<TSource>
显然,源集合。转换函数是编译器Func<Tsource, TResult>
可以使用类型推断来声明匿名类型的地方。
换句话说,为了将 a 传递Func<Tsource, TResult>
给Select
,您(调用者)必须定义TResult
。这意味着Select
不是返回由它定义的匿名类型 - 而是由您定义的。
要模拟这一点,您只需让调用者定义类型:
TResult ReturnAnonymousType<TResult>(Func<TResult> f) {
return f();
}
Console.WriteLine(ReturnAnonymousType(
() => return new { Text = "Hello World!" } // type defined here, before calling
);
来自评论:“那么我将如何实施类似的方法”
您需要的只是任何通用方法:
public List<T> Foo<T>(T template) { // doesn't actually use "template"
return new List<T>(); // just an example
}
那么你可以拥有:
var list = Foo(new {Bar=1});
编译器提供<T>
通过泛型类型推断。
有点厚颜无耻,但你甚至可以在没有实际创建匿名类型实例的情况下做到这一点:
public List<T> Foo<T>(Func<T> func) { // doesn't actually use "func"
return new List<T>(); // just an example
}
var list = Foo(() => new {Bar = 1});
同样,它是由编译器通过 lambda 的返回值提供的。
Select 的返回类型是通用的,它是从大多数情况下提供的 lambda 推断出来的。
例如:
List<int> list = new List<int<();
var val = list.Select(x => new {value = x, mod = x % 10});
select 的返回值基于我定义的匿名类型,并从 lambda 推断到委托,再到 Select 函数。在这种情况下,Select 函数不知道也不关心特定的匿名类型,因为从它的角度来看,它是一个泛型类型。