方法和匿名类型

4

我知道你不能从方法中返回匿名类型,但我想知道Select扩展方法如何返回匿名类型。这只是编译器的技巧吗?

编辑

假设L是一个List,这是如何工作的?

L.Select(s => new { Name = s })

返回类型是IEnumerable<'a>,其中'a = new {String Name}'。
4个回答

8

这是泛型方法类型参数的正常类型推断。例如:

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表达式的推断返回类型是一个匿名类型,同样适用。不要忘记,即使您不能显式地声明使用匿名类型的变量的类型,它仍然具有明确的类型,并且编译器知道它。

其实,你能有一个匿名类型的列表吗?我认为他说的是当你的示例中使用匿名类型而不是int时。 - Guvante
是的,你可以轻松地拥有一个带有匿名类型元素的列表:new[] { new { Name="Jon" } }.ToList(); - Jon Skeet
还有其他获取列表的方法 - 例如,编写扩展方法并使用单个匿名实例(或lambda表达式)将T转换为List<T>。 - Marc Gravell

8

这种类型实际上是由调用方定义的,因此它在调用函数的范围内 - 这样就巧妙地避免了“返回”匿名类型的问题。

这是通过泛型类型推断实现的。 Select的签名是Select<Tsource, TResult>(IEnumerable<TSource>, Func<TSource, TResult>。显然,IEnumerable<TSource>是源集合。 Func<Tsource, TResult>转换函数是编译器可以使用类型推断声明匿名类型的地方。

换句话说,为了将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 
);

5

来自评论:“那我该怎么实现类似的方法?”

这里你只需要任何通用方法:

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的返回值,编译器提供了 。


1

Select的返回类型是通用的,并且在大多数情况下从提供的lambda中推断出来。

例如:

List<int> list = new List<int<();

var val = list.Select(x => new {value = x, mod = x % 10});

select语句的返回值基于我定义的匿名类型,并从lambda表达式推导出委托,再传递给Select函数。在这种情况下,Select函数并不知道或关心特定的匿名类型,因为从它的角度来看,它是一个通用类型。


谢谢Guvante。那么我该如何实现类似的方法呢? - Rodrick Chapman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接