如何从匿名类型引用项目

时间:2011-12-08 20:40:41

标签: c# linq anonymous-types

我有这样的代码

var results =   (from c in Customers  
                    join o in Orders  
                    on c.Id equals o.CustomerId  
                    join p in Products  
                    on p.Id equals o.ProductId  
                    select new   
                    {  
                        CustomerId = c.Id,     // this is a GUID  
                        OrderId = o.Id,        // this is a GUID    
                        ProductName = p.ProductName,  
                     }).ToList();  

让我们说我想获得一个订购名称= foo的产品的所有客户ID列表 我的问题是,因为它是一个匿名类型,我如何在任何想要在结果上运行的Linq查询中引用产品名称?

4 个答案:

答案 0 :(得分:5)

var filteredResults = results.Where(r => r.ProductName == "X");

编译器的类型推断会为您处理。你问题的完整答案:

var customerIds = results
    .Where(r => r.ProductName == "X")
    .Select(r => r.CustomerId)
    .Distinct()
    .ToList();

var customerIds = (from r in results
                  where r.ProductName == "X"
                  select r.CustomerId)
    .Distinct()
    .ToList();

修改

关于类型推断的一些思考

要从名为list的字符串序列中选择长度,您可以使用经典静态方法语法或扩展方法调用Select

Enumerable.Select<string, int>(list, s => s.Length)
list.Select<string, int>(s => s.Length)

由于类型推断,您不需要类型参数:

Enumerable.Select(list, s => s.Length)
list.Select(s => s.Length)

在这种情况下,编译器可以通过查看方法参数来证明类型参数是stringint,并且代表您提供这些类型参数,而无需将它们键入源代码。

对于匿名类型,不能提供第一个类型参数,因为该类型没有您在源代码中使用的名称(即毕竟“匿名”意味着什么:“没有名字”)。 (因此,您可以看到匿名类型和类型推断都是关键 - 并且密切相关 - 首先实现linq的先决条件。)

如果您查看上面的匿名类型示例的IL,您将看到编译器实际上为该类型指定了一个名称(其中包含在C#标识符中非法的字符)。当你调用Select时,编译器会从可枚举的类型(IEnumerable<CrazilyNamedAnonymousType>)中推断出第一个类型的参数应该是匿名类型,并且与字符串示例一样,它会为你提供该值。代表。

答案 1 :(得分:2)

在生成此匿名类型结果的方法中,您可以继续引用结果,就像定义了具体类型一样。

var customersWithFoo = results.Where(r => r.ProductName == "foo")
                              .Select(r => r.CustomerId);

如果要返回此方法的原始查询结果 out 然后希望进一步查询或以编程方式访问元素,定义类型。

class QueryResult 
{
   /* relevant properties */
}

然后在原始查询中投射到该类型,并从您的方法返回该类型的序列。

public IEnumerable<QueryResult> GetResults()
{
     var results = ...
                   select new QueryResult 
                   {
                       // properties
                   };

     return results;
}

答案 2 :(得分:2)

跟进你的评论:

  

我对匿名类型的范围感到困惑

首先让我们清楚地定义“范围”。 类型范围被定义为程序文本的区域,其中的类型可以通过其非限定名称来引用。

使用该定义很明显匿名类型的范围是什么。程序文本中有 no 区域,其中匿名类型可以通过其名称​​引用,因为它没有名称。匿名类型根本没有范围。你不必担心它的范围;它没有范围。

匿名类型的字段也没有范围,但原因不同。匿名类型的字段具有名称,但通过其非限定名称引用它们从不合法,因此每个字段的范围都是空的。

  

我不确定在方法或类范围内这种类型的可见性。

再次,让我们清楚地定义我们的术语。实体的范围可以包括定义声明空间的实体。这些声明空间可以声明与原始实体具有相同名称的实体。这些实体有自己的范围,可以嵌套在原始实体的范围内。

在这种情况下,更嵌套的实体可能会“隐藏”较少嵌套的实体。以这种方式隐藏的实体被称为在特定文本位置“可见”。

匿名类型没有范围,显然不能通过名称隐藏它,因为它没有名称。询问匿名类型是否“可见”并不是一件明智的事情; “可见性”仅对具有名称的事物有意义。

  

我的想法是,鉴于此类型未在任何地方声明,编译器如何确定我在谈论哪种类型?

编译器会记录您在程序中使用匿名类型的每个文本位置。如果这些位置中的任何两个引用具有相同字段名称的匿名类型,则相同的字段类型和字段以相同的顺序然后将这两个位置视为相同匿名类型的用法。

然后,编译器可以为您在程序集中使用的每个唯一匿名类型发出一种类型。它是如何做到的细节是令人着迷的(*)。我建议您使用ILDASM在您的装配周围逛一逛,看看如果您有兴趣我们如何做。

如果你在两个不同的程序集中制作“相同”的匿名类型 - 相同的名称,类型和相同的顺序,那么匿名类型将被视为同一类型。匿名类型不是为跨程序集边界使用而设计的。


(*)给我。

答案 3 :(得分:0)

匿名类型将显示在intellisense中,就像“普通”类型一样。在编译时,会创建一个表示您的匿名类型的具体类,因此在运行时几乎没有差别。

以下是对结果集的查询:

var fooOrders = (from x in results 
                where x.ProductName == "foo"
                select x.CustomerId);