相关文章推荐
曾深爱过的八宝粥  ·  Flink ...·  1 年前    · 
奔放的包子  ·  python 包 降级 ...·  1 年前    · 
高兴的丝瓜  ·  VBA字典 - ilovetesting ...·  1 年前    · 
面冷心慈的马铃薯  ·  javascript - ...·  2 年前    · 

投影是指将对象转换为一种新形式的操作,该形式通常只包含那些将随后使用的属性。 通过使用投影,您可以构造从每个对象生成的新类型。 可以投影属性,并对该属性执行数学函数。 还可以在不更改原始对象的情况下投影该对象。

下面一节列出了执行投影的标准查询运算符方法。

C# 查询表达式语法

Select

下面的示例使用 select 子句来投影字符串列表中每个字符串的第一个字母。

List<string> words = new() { "an", "apple", "a", "day" };
var query = from word in words
            select word.Substring(0, 1);
foreach (string s in query)
    Console.WriteLine(s);
/* This code produces the following output:

SelectMany

下面的示例使用多个 from 子句来投影字符串列表中每个字符串中的每个单词。

List<string> phrases = new() { "an apple a day", "the quick brown fox" };
var query = from phrase in phrases
            from word in phrase.Split(' ')
            select word;
foreach (string s in query)
    Console.WriteLine(s);
/* This code produces the following output:
    apple
    quick
    brown

Zip 投影运算符有多个重载。 所有 Zip 方法都处理两个或更多可能是异构类型的序列。 前两个重载返回元组,具有来自给定序列的相应位置类型。

请考虑下列集合:

// An int array with 7 elements. IEnumerable<int> numbers = new[] 1, 2, 3, 4, 5, 6, 7 // A char array with 6 elements. IEnumerable<char> letters = new[] 'A', 'B', 'C', 'D', 'E', 'F'

若要将这些序列一起投影,请使用 Enumerable.Zip<TFirst,TSecond>(IEnumerable<TFirst>, IEnumerable<TSecond>) 运算符:

foreach ((int number, char letter) in numbers.Zip(letters)) Console.WriteLine($"Number: {number} zipped with letter: '{letter}'"); // This code produces the following output: // Number: 1 zipped with letter: 'A' // Number: 2 zipped with letter: 'B' // Number: 3 zipped with letter: 'C' // Number: 4 zipped with letter: 'D' // Number: 5 zipped with letter: 'E' // Number: 6 zipped with letter: 'F'

zip 操作生成的序列的长度永远不会长于最短序列。 numbersletters 集合的长度不同,生成的序列将省略 numbers 集合中的最后一个元素,因为它没有任何要压缩的内容。

第二个重载接受 third 序列。 让我们创建另一个集合,即 emoji

// A string array with 8 elements. IEnumerable<string> emoji = new[] "🤓", "🔥", "🎉", "👀", "⭐", "💜", "✔", "💯"

若要将这些序列一起投影,请使用 Enumerable.Zip<TFirst,TSecond,TThird>(IEnumerable<TFirst>, IEnumerable<TSecond>, IEnumerable<TThird>) 运算符:

foreach ((int number, char letter, string em) in numbers.Zip(letters, emoji)) Console.WriteLine( $"Number: {number} is zipped with letter: '{letter}' and emoji: {em}"); // This code produces the following output: // Number: 1 is zipped with letter: 'A' and emoji: 🤓 // Number: 2 is zipped with letter: 'B' and emoji: 🔥 // Number: 3 is zipped with letter: 'C' and emoji: 🎉 // Number: 4 is zipped with letter: 'D' and emoji: 👀 // Number: 5 is zipped with letter: 'E' and emoji: ⭐ // Number: 6 is zipped with letter: 'F' and emoji: 💜

与前面的重载非常相似,Zip 方法投影一个元组,但这次包含三个元素。

第三个重载接受用作结果选择器的 Func<TFirst, TSecond, TResult> 参数。 鉴于正在压缩的序列中的两种类型,可以投影一个新的生成序列。

foreach (string result in numbers.Zip(letters, (number, letter) => $"{number} = {letter} ({(int)letter})")) Console.WriteLine(result); // This code produces the following output: // 1 = A (65) // 2 = B (66) // 3 = C (67) // 4 = D (68) // 5 = E (69) // 6 = F (70)

使用前面的 Zip 重载,指定的函数应用于相应的元素 numbersletter,生成 string 结果的序列。

SelectSelectMany

SelectSelectMany 的工作都是依据源值生成一个或多个结果值。 Select 为每个源值生成一个结果值。 因此,总体结果是一个与源集合具有相同元素数目的集合。 与之相反,SelectMany 生成单个总体结果,其中包含来自每个源值的串联子集合。 作为参数传递到 SelectMany 的转换函数必须为每个源值返回一个可枚举值序列。 然后,SelectMany 串联这些可枚举序列,以创建一个大的序列。

下面两个插图演示了这两个方法的操作之间的概念性区别。 在每种情况下,假定选择器(转换)函数从每个源值中选择一个由花卉数据组成的数组。

下图描述 Select 如何返回一个与源集合具有相同元素数目的集合。

下图描述 SelectMany 如何将中间数组序列串联为一个最终结果值,其中包含每个中间数组中的每个值。

下面的示例比较 SelectSelectMany 的行为。 代码通过从源集合的每个花卉名称列表中提取项来创建一个“花束”。 此示例中,transform 函数 Select<TSource,TResult>(IEnumerable<TSource>, Func<TSource,TResult>) 使用的“单值”本身即是值的集合。 这需要额外的 foreach 循环,以便枚举每个子序列中的每个字符串。

class Bouquet
    public List<string> Flowers { get; set; }
static void SelectVsSelectMany()
    List<Bouquet> bouquets = new()
        new Bouquet { Flowers = new List<string> { "sunflower", "daisy", "daffodil", "larkspur" }},
        new Bouquet { Flowers = new List<string> { "tulip", "rose", "orchid" }},
        new Bouquet { Flowers = new List<string> { "gladiolis", "lily", "snapdragon", "aster", "protea" }},
        new Bouquet { Flowers = new List<string> { "larkspur", "lilac", "iris", "dahlia" }}
    IEnumerable<List<string>> query1 = bouquets.Select(bq => bq.Flowers);
    IEnumerable<string> query2 = bouquets.SelectMany(bq => bq.Flowers);
    Console.WriteLine("Results by using Select():");
    // Note the extra foreach loop here.
    foreach (IEnumerable<String> collection in query1)
        foreach (string item in collection)
            Console.WriteLine(item);
    Console.WriteLine("\nResults by using SelectMany():");
    foreach (string item in query2)
        Console.WriteLine(item);
    /* This code produces the following output:
       Results by using Select():
        sunflower
        daisy
        daffodil
        larkspur
        tulip
        orchid
        gladiolis
        snapdragon
        aster
        protea
        larkspur
        lilac
        dahlia
       Results by using SelectMany():
        sunflower
        daisy
        daffodil
        larkspur
        tulip
        orchid
        gladiolis
        snapdragon
        aster
        protea
        larkspur
        lilac
        dahlia
  • System.Linq
  • 标准查询运算符概述 (C#)
  • select 子句
  • 如何从多个源填充对象集合 (LINQ) (C#)
  • 如何使用组将一个文件拆分成多个文件 (LINQ) (C#)
  •