LINQ 查询作中的类型关系 (C#)

若要有效地编写查询,应了解完整查询作中变量的类型如何相互关联。 如果你了解这些关系,你将更容易理解文档中的 LINQ 示例和代码示例。 此外,你将了解在使用 var隐式类型化变量时会发生什么情况。

LINQ 查询操作在数据源、查询本身及查询执行中是强类型的。 查询中的变量类型必须与数据源中的元素的类型以及语句中的 foreach 迭代变量的类型兼容。 此强类型保证在编译时捕获类型错误,以便可以在用户遇到这些错误之前更正它们。

为了演示这些类型关系,后面的大多数示例都对所有变量使用显式键入。 最后一个示例演示在利用使用 var 的隐式类型时,如何应用相同的原则。

不对源数据进行转换的查询

下图显示了对数据不执行任何转换的 LINQ to Objects 查询作。 源包含字符串序列,查询输出也是字符串序列。

显示 LINQ 查询中数据类型的关系的关系图。

  1. 数据源的类型参数确定范围变量的类型。
  2. 所选对象的类型确定查询变量的类型。 下面是 name 一个字符串。 因此,查询变量是一个 IEnumerable<string>
  3. foreach语句中迭代查询变量。 由于查询变量是字符串序列,因此迭代变量也是字符串。

改变源数据的查询

下图显示了一个 LINQ to SQL 查询操作,该操作对数据进行简单转换。 查询采用一系列 Customer 对象作为输入,结果中仅选择 Name 属性。 由于 Name 是字符串,因此查询将生成一系列字符串作为输出。

关系图显示转换数据类型的查询。

  1. 数据源的类型参数确定范围变量的类型。
  2. select 语句返回 Name 属性而不是完整的 Customer 对象。 因为Name是字符串,类型参数是custNameQuery而不是string
  3. 由于 custNameQuery 是字符串序列,因此 foreach 循环的迭代变量也必须是一个 string

下图显示了稍微复杂一些的转换。 该 select 语句返回一个匿名类型,该类型只捕获原始 Customer 对象的两个成员。

关系图显示转换数据类型的更复杂的查询。

  1. 数据源的类型参数始终是查询中范围变量的类型。
  2. 因为 select 语句生成匿名类型,所以必须使用 var 隐式类型化查询变量。
  3. 由于查询变量的类型是隐式的,因此循环中的 foreach 迭代变量也必须是隐式的。

让编译器推断类型信息

尽管你应该了解查询作中的类型关系,但可以选择让编译器为你完成所有工作。 关键字 var 可用于查询作中的任何局部变量。 下图类似于前面讨论的示例 2。 但是,编译器为查询作中的每个变量提供强类型。

关系图显示具有隐式类型的类型流。

LINQ 和泛型类型 (C#)

LINQ 查询基于泛型类型。 在开始编写查询之前,无需深入了解泛型。 但是,你可能想要了解两个基本概念:

  1. 创建泛型集合类 List<T>的实例时,请将“T”替换为列表将保留的对象类型。 例如,字符串列表表示为 List<string>,对象列表 Customer 表示为 List<Customer>。 泛型列表是强类型化的,它比存储其元素为 Object 类型的集合具有更多优点。 如果尝试将Customer添加到List<string>,将在编译时收到错误。 使用泛型集合很容易,因为无需执行运行时类型强制转换。
  2. IEnumerable<T> 是允许使用语句枚举泛型集合类的 foreach 接口。 泛型集合类支持 IEnumerable<T>,就像非泛型集合类(例如 ArrayList)支持 IEnumerable一样。

有关泛型的详细信息,请参阅 泛型

LINQ 查询中的 IEnumerable<T> 变量

LINQ 查询变量被定义为 IEnumerable<T> 类型或某种派生类型,例如 IQueryable<T>。 当看到类型化为 IEnumerable<Customer>查询变量时,它只是意味着执行查询时,将生成零个或多个 Customer 对象的序列。

IEnumerable<Customer> customerQuery = from cust in customers
                                      where cust.City == "London"
                                      select cust;

foreach (Customer customer in customerQuery)
{
    Console.WriteLine($"{customer.LastName}, {customer.FirstName}");
}

让编译器处理泛型类型声明

如果愿意,可以使用 var 关键字来避免泛型语法。 关键字 var 指示编译器通过查看子句中指定的 from 数据源来推断查询变量的类型。 以下示例生成与上一示例相同的已编译代码:

var customerQuery2 = from cust in customers
                     where cust.City == "London"
                     select cust;

foreach(var customer in customerQuery2)
{
    Console.WriteLine($"{customer.LastName}, {customer.FirstName}");
}

当变量的类型明显或显式指定嵌套泛型类型(例如组查询生成的类型)并不重要时,关键字 var 非常有用。 一般来说,我们建议如果你使用var,要意识到这可能会使你的代码更难让他人阅读。 有关详细信息,请参阅 隐式类型局部变量