65.9K
CodeProject 正在变化。 阅读更多。
Home

LINQ to Objects

starIconstarIconemptyStarIconemptyStarIconemptyStarIcon

2.00/5 (3投票s)

2007年12月18日

CPOL

7分钟阅读

viewsIcon

18918

LINQ to Objects 帮助我们使用 LINQ 查询集合中的对象。

引言

LINQ to Objects 帮助我们使用 LINQ 查询集合中的对象。我们可以使用 LINQ 访问内存中的数据结构。我们可以查询实现 IEnumerable 接口或泛型类型 IEnumerable 的任何类型的对象。列表、数组和字典是一些可以使用 LINQ 查询的集合对象。

在本文中,作者 N. Satheesh Kumar 向我们展示了如何使用 LINQ 运算符查询不同的对象,并避免使用循环方法来过滤集合中的值。

没有 LINQ,我们就必须逐个遍历值,然后查找所需详细信息。然而,使用 LINQ,我们可以直接查询集合并过滤所需值,而无需任何循环。LINQ 提供了强大的筛选、排序和分组功能,所需的编码最少。例如,如果我们想找出存储在程序集中的类型,然后筛选所需详细信息,我们可以使用 LINQ 通过 System.Reflection 类来查询程序集详细信息。System.Reflection 命名空间包含通过检查其元数据来检索有关程序集、模块、成员、参数和其他实体的类型。此外,目录下的文件是可以使用 LINQ 查询的对象集合。我们将看到一些查询集合的示例。

整数数组

以下示例显示了一个包含一组整数的整数数组。我们可以对数组应用 LINQ 查询来获取所需值。

int[] integers = { 1, 6, 2, 27, 10, 33, 12, 8, 14, 5 };
IEnumerable<int> twoDigits = from numbers in integers
        where numbers >= 10
        select numbers;
Console.WriteLine("Integers > 10:");
foreach (var number in twoDigits)
{
    Console.WriteLine(number);
}

integers 变量包含一个具有不同值的整数数组。twoDigits 变量(类型为 IEnumerable)保存查询。要获得实际结果,必须执行查询。

当查询变量通过 foreach 循环迭代,通过调用 GetEnumerator() 枚举结果时,会发生实际的查询执行。类型为 IEnumerable<T> 的任何变量都可以使用 foreach 结构进行枚举。支持 IEnumerable<T> 或派生接口(如泛型 IQueryable<T>)的类型称为可查询类型。所有集合,如列表、字典和其他类,都是可查询的。存在一些非泛型 IEnumerable 集合,如 ArrayList,也可以使用 LINQ 进行查询。为此,我们必须将范围变量的类型显式声明为集合中对象的特定类型,正如本文后面的示例中所解释的那样。

twoDigits 变量将保存查询,以获取大于或等于 10 的值。这用于逐个从数组中获取数字。foreach 循环将执行查询,然后遍历从整数数组中检索到的值,并将它们写入控制台。这是从集合中获取所需值的简单方法。

如果我们只想要集合中的前四个值,我们可以对集合对象应用 Take() 查询运算符。以下示例从集合中获取前四个整数。结果集合中的四个整数使用 foreach 方法显示。

IEnumerable<int> firstFourNumbers = integers.Take(4);
Console.WriteLine("First 4 numbers:");
foreach (var num in firstFourNumbers)
{
    Console.WriteLine(num);
}

Take() 运算符的相反是 Skip() 运算符,它用于跳过集合中的项目数并检索其余项。以下示例跳过列表中的前四个项目并检索其余项。

IEnumerable<int> skipFirstFourNumbers = integers.Skip(4);
Console.WriteLine("Skip first 4 numbers:");
foreach (var num in skipFirstFourNumbers)
{
  Console.WriteLine(num);
}

此示例显示了一种从集合中获取或跳过指定数量项目的方法。那么,如果我们想在列表中找到匹配项之前跳过或获取项目,该怎么办?我们有相应的运算符。它们是 TakeWhile()SkipWhile()

例如,以下代码显示了如何从整数集合中获取数字列表,直到找到 50。TakeWhile() 使用表达式,只要条件为真,就将元素包含在集合中,并忽略列表中的其他元素。此表达式表示用于测试集合中元素的条件以匹配。

int[] integers = { 1, 9, 5, 3, 7, 2, 11, 23, 50, 41, 6, 8 };
IEnmerable<int> takeWhileNumber = integers.TakeWhile(num =>
num.CompareTo(50) != 0);
Console.WriteLine("Take while number equals 50");
foreach (int num in takeWhileNumber)
{
    Console.WriteLine(num.ToString());
}

类似地,我们可以使用 SkipWhile() 跳过集合中的项目。它使用表达式,只要条件为真,就绕过集合中的元素。此表达式用于评估列表中每个元素的条件。表达式的输出是布尔值。如果表达式返回 false,则返回集合中的其余元素,并且不会为其他元素执行该表达式。返回值为 false 的第一个出现会停止对其他元素的表达式执行,并返回其余元素。当第一个匹配项找到后,这些运算符如果应用于排序列表,会提供更好的结果,因为表达式会被忽略。

IEnumerable<int> skipWhileNumber = integers.SkipWhile(num =>
num.CompareTo(50) != 0);
Console.WriteLine("Skip while number equals 50");
foreach (int num in skipWhileNumber)
{
    Console.WriteLine(num.ToString());
}

对象集合

在本节中,我们将看到如何查询自定义构建的对象集合。让我们以 Icecream 对象为例,构建一个集合,然后我们可以查询该集合。以下代码中的 Icecream 类包含不同的属性,如 NameIngredientsTotalFatCholesterol

public class Icecream
{
    public string Name { get; set; }
    public string Ingredients { get; set; }
    public string TotalFat { get; set; }
    public string Cholesterol { get; set; }
    public string TotalCarbohydrates { get; set; }
    public string Protein { get; set; }
    public double Price { get; set; }
}

现在,使用前面定义的类构建 Icecreams 列表集合。

List<Icecream> icecreamsList = new List<Icecream>
{
    new Icecream {Name="Chocolate Fudge Icecream", Ingredients="cream,
    milk, mono and diglycerides...", Cholesterol="50mg",
    Protein="4g", TotalCarbohydrates="35g", TotalFat="20g",
    Price=10.5
},
    new Icecream {Name="Vanilla Icecream", Ingredients="vanilla extract,
    guar gum, cream...", Cholesterol="65mg", Protein="4g",
    TotalCarbohydrates="26g", TotalFat="16g", Price=9.80 },
    new Icecream {Name="Banana Split Icecream", Ingredients="Banana, guar
    gum, cream...", Cholesterol="58mg", Protein="6g",
    TotalCarbohydrates="24g", TotalFat="13g", Price=7.5 }
};

我们拥有 icecreamsList 集合,其中包含三个 Icecream 类型的对象。现在,假设我们需要检索所有价格较低的冰淇淋。我们可以使用循环方法,其中我们必须逐个查看列表中每个对象的价格值,然后检索 Price 属性值较低的对象。使用 LINQ,我们可以避免循环遍历所有对象及其属性来查找所需的对象。我们可以使用 LINQ 查询轻松找到这一点。以下是一个从集合中获取低价冰淇淋的查询。该查询使用 where 条件来执行此操作。这类似于关系数据库查询。当类型为 IEnumerable 的变量在 foreach 循环中被引用时,查询将被执行。

List<Icecream> Icecreams = CreateIcecreamsList();
IEnumerable<Icecream> IcecreamsWithLessPrice =
from ice in Icecreams
where ice.Price < 10
select ice;
Console.WriteLine("Ice Creams with price less than 10:");
foreach (Icecream ice in IcecreamsWithLessPrice)
{
    Console.WriteLine("{0} is {1}", ice.Name, ice.Price);
}

正如我们使用了 List<Icecream> 对象一样,我们也可以使用 ArrayList 来保存对象,并且可以使用 LINQ 查询来根据我们的需要从集合中检索特定的对象。例如,以下代码将与我们在上一个示例中一样,将相同的 Icecreams 对象添加到 ArrayList 中。

ArrayList arrListIcecreams = new ArrayList();
arrListIcecreams.Add( new Icecream {Name="Chocolate Fudge Icecream",
   Ingredients="cream, milk, mono and diglycerides...",
   Cholesterol="50mg", Protein="4g", TotalCarbohydrates="35g",
   TotalFat="20g", Price=10.5 });
arrListIcecreams.Add( new Icecream {Name="Vanilla Icecream",
   Ingredients="vanilla extract, guar gum, cream...",
   Cholesterol="65mg", Protein="4g", TotalCarbohydrates="26g",
   TotalFat="16g", Price=9.80 });
arrListIcecreams.Add( new Icecream {Name="Banana Split Icecream",
   Ingredients="Banana, guar gum, cream...", Cholesterol="58mg",
   Protein="6g", TotalCarbohydrates="24g", TotalFat="13g", Price=7.5
});

以下是从列表中获取低价冰淇淋的查询。

var queryIcecreanList = from Icecream icecream in arrListIcecreams
where icecream.Price < 10
select icecream;

使用下面的 foreach 循环来显示使用上述查询检索到的对象的 PPrice。

foreach (Icecream ice in queryIcecreanList)
Console.WriteLine("Icecream Price : " + ice.Price);

从字符串读取

我们都知道字符串是字符的集合。这意味着我们可以直接查询字符串值。现在,让我们看一个字符串值,并尝试找出字符串中大写字母的数量。例如,将一个字符串值赋给变量 aString,如下所示。

string aString = "Satheesh Kumar";

现在,让我们构建一个查询来读取字符串并找出大写字母的数量。查询的类型应为 IEnumerable

IEnumerable<char> query =
from ch in aString
where Char.IsUpper(ch)
select ch;

查询在 where 子句中使用 Char.IsUpper 方法来找出字符串中的大写字母。以下代码显示了字符串中大写字符的数量。

Console.WriteLine("Count = {0}", count);

从文本文件读取

无论文件包含什么数据,都可以称之为集合。让我们创建一个包含字符串集合的文本文件。为了从文本文件中获取值,我们可以使用 LINQ 查询。创建一个包含不同冰淇淋名称的文本文件。我们可以使用 StreamReader 对象从文本文件中逐行读取。创建一个 List 对象,其类型为 string,用于保存从文本文件中读取的值。一旦我们将值加载到 List 中,我们就可以像处理普通集合对象一样,使用 LINQ 查询轻松地查询该列表。以下示例代码读取文本文件,并将冰淇淋名称加载到字符串列表中。

List<string> IcecreamNames = new List<string>();
using( StreamReader sReader = new StreamReader(@"C:Icecreams.txt"))
{
    string str;
    str = sReader.ReadLine();
    while (str != null)
    {
        IcecreamNames.Add(str);
    }
}

以下示例代码读取字符串列表,并按降序检索冰淇淋的名称。

IEnumerable<string> icecreamQuery =
from name in IcecreamNames
orderby name descending
select name;

我们可以使用以下代码显示名称来验证查询结果。

foreach (string nam in icecreamQuery)
{
    Console.WriteLine(nam);
}

以下代码显示名称并验证查询结果。

foreach (string nam in icecreamQuery)
{
    Console.WriteLine(nam);
}

与上面示例中使用的集合类似,.NET 反射类库可用于读取 .NET 程序集的元数据,并将其类型、类型成员、参数和其他属性创建为集合。这些集合支持 IEnumerable 接口,这有助于我们使用 LINQ 查询它们。

摘要

在本文中,我们看到了一些使用 LINQ 运算符查询不同对象的示例。我们可以在任何支持 IEnumerable 的对象上使用 LINQ 查询。通过使用 LINQ,我们可以避免使用循环方法遍历集合并获取所需详细信息。LINQ 提供了强大的筛选、排序和分组方法。这将减少我们的编码以及开发时间。

© . All rights reserved.