写点什么

【C#】System.Linq,万能的查询语句

作者:萧然🐳
  • 2022 年 7 月 09 日
  • 本文字数:9920 字

    阅读完需:约 33 分钟

首先是官方文档:

System.Linq 命名空间 | Microsoft Docs

Enumerable 类 (System.Linq) | Microsoft Docs


合理的使用 System.Linq 类库,可以让代码更加高效简洁,对操作继承 IEnumerable 的数据类型(数组、List、Dictionary 等)可以快速高效的做一些操作,比如在字典中根据 Value 值获取对应的 Key,常规方法就是遍历比较,使用 Linq 库的"FirstOrDefault"或"Whrer"则会更加快捷。


Linq 查询有两种形式的语法:查询语法方法语法


1. 查询语法:使用查询表达式的形式,类似 SQL 语句

        var value00 = from item in array1 select item;
复制代码


2. 方法语法:使用方法调用的形式

        value00 = array1.Select(item => item);
复制代码


下面是示例:


一、准备工作

先准备几个测试类

    public class PlayerGroup    {        public Player[] players;    }
public class EnemyGroup { public Enemy[] enemies; }
public class Player { public Player(string _name, int _level) { name = _name; level = _level; attack = level / 2 * 10; }
public string name; public int level; public int attack; }
public class Enemy { public Enemy(string _name, int _level) { name = _name; level = _level; attack = level / 2 * 10; }
public string name; public int level; public int attack; }
复制代码


在准备几个测试数组(其他实现了 IEnumerable<>接口的系列也可)

int[] array1 = new int[] { 0, 1, 2, 3, 4, };        int[] array2 = new int[] { 5, 6, 7, 8, 9, };
Player[] array3 = new Player[] { new Player("p01", 1), new Player("p02", 2), new Player("p03", 3), new Player("p04", 4), new Player("p05", 5), new Player("p06", 6), new Player("p07", 7), new Player("p08", 8), new Player("p09", 9), new Player("p10", 10), };
Enemy[] array4 = new Enemy[] { new Enemy("e01", 6), new Enemy("e02", 7), new Enemy("e03", 8), new Enemy("e04", 9), new Enemy("e05", 10), new Enemy("e06", 11), new Enemy("e07", 12), new Enemy("e08", 13), new Enemy("e09", 14), new Enemy("e10", 15), };
PlayerGroup[] array5 = new PlayerGroup[2] { new PlayerGroup() { players = new Player[]{ new Player("pm11", 1), new Player("pm12", 1) } }, new PlayerGroup() { players = new Player[]{ new Player("pm21", 2), new Player("pm22", 2) } } };
EnemyGroup[] array6 = new EnemyGroup[2] { new EnemyGroup() { enemies = new Enemy[]{ new Enemy("em11", 2), new Enemy("em12", 2) } }, new EnemyGroup() { enemies = new Enemy[]{ new Enemy("em21", 3), new Enemy("em22", 3) } } };
int[] array7 = new int[] { 1, 2, 2, 3, 4, 5, 5, 6 };
复制代码


为了方便查看结果,对序列元素的 log 做一下处理

    private void TestLog(IEnumerable enumerable)    {        if (enumerable == null)            Debug.Log("enumerable is null");        else        {            string logStr = "";            foreach (var item in enumerable)            {                logStr += " - " + item;            }            Debug.Log(logStr);        }    }
复制代码


二、查询语法的查询子句,以及部分对应的方法语法的运算


1. from:指定要作为数据来源使用的数据集合


        //from:指定要作为数据来源使用的数据集合        //指定以array1作为数据来源        var value01 = from item in array1                      select item;        TestLog(value01);        // - 0 - 1 - 2 - 3 - 4
//from可以有多个,每个from子句都指定了一个额外的源数据合集 //指定array3,array4两个源数据 var value02 = from item1 in array3 from item2 in array4 where item1.level == item2.level select item1.name; TestLog(value02); // - p06 - p07 - p08 - p09 - p10
复制代码


2. select:指定对象的哪部分被选择(可以是整个数据项,也可以是其中的一个或几个字段),并将结果序列合并为一个序列

        //指定选择name字段,返回值类型var实际为IEnumerable<string>        var value03 = from item in array3                      select item.name;        TestLog(value03);        value03 = array3.Select(item => item.name);        TestLog(value03);        // - p01 - p02 - p03 - p04 - p05 - p06 - p07 - p08 - p09 - p10
//select:指定选择name和attack字段,返回值类型var实际为IEnumerable<t>,t是匿名类型new{ int level, int attack } var value04 = from item in array3 where item.level < 5 select new { item.level, item.attack }; TestLog(value04); value04 = array3.Select(item => new { item.level, item.attack }); TestLog(value04); // - { level = 1, attack = 0 } - { level = 2, attack = 10 } - { level = 3, attack = 10 } - { level = 4, attack = 20 }
复制代码


3. where:对序列的元素进行筛选

        //where:对array1的元素进行筛选,只取level < 3的元素        var value05 = from item in array1                      where item < 3                      select item;        TestLog(value05);        value05 = array1.Where(item => item < 3);        TestLog(value05);        // - 0 - 1 - 2
复制代码


4. let:接受一个表达式的运算,并赋记录结果

        //let:记录两个元素之和        var value06 = from item1 in array1                      from item2 in array2                      let sum = item1 + item2                      where sum == 10                      select item1 + "+" + item2 + "=10";        TestLog(value06);        // - 1+9=10 - 2+8=10 - 3+7=10 - 4+6=10
复制代码


5. orderby:根据一个或多个键对序列中的元素排序

        //orderby:按照level进行排序        var value07 = from item in array3                      where item.level <= 5                      orderby item.level                      select item.name;        TestLog(value07);        // - p01 - p02 - p03 - p04 - p05
复制代码


6. group...by...:指定选择的项如何被分组

        //group...by...:按照attack进行分组        var value08 = from item in array3                      group item by item.attack;        TestLog(value08.Select(item => item.Key + ":" + item.Count()));        // - 0:1 - 10:2 - 20:2 - 30:2 - 40:2 - 50:1
复制代码


7. join...on...:联结两个序列,作为一个新的集合

        //join...on...:以array1和array2作为一个新的集合,并取其中array1元素中和array2元素中level相等的元素        var value09 = from item1 in array3                      join item2 in array4 on item1.level equals item2.level                      select item1.level;        TestLog(value09);        value09 = array3.Join(array4, item1 => item1.level, item2 => item2.level, (item1, item2) => item1.level);        TestLog(value09);        // - 6 - 7 - 8 - 9 - 10
//join...on... var value10 = from item1 in array3 join item2 in array4 on item1.level equals item2.level select new { item1.level, playerName = item1.name, enemyName = item2.name }; TestLog(value10); value10 = array3.Join(array4, item1 => item1.level, item2 => item2.level, (item1, item2) => new { item1.level, playerName = item1.name, enemyName = item2.name }); TestLog(value10); // - { level = 6, playerName = p06, enemyName = e01 } - { level = 7, playerName = p07, enemyName = e02 } - { level = 8, playerName = p08, enemyName = e03 } - { level = 9, playerName = p09, enemyName = e04 } - { level = 10, playerName = p10, enemyName = e05 }
复制代码


8. into:可以记录查询的一部分结果,以便后续继续使用

        //into:可以记录查询的一部分结果,以便后续继续使用        var value11 = from item1 in array3                      join item2 in array4 on item1.level equals item2.level                      into temp                      from item3 in temp                      select item3.level;        TestLog(value11);        // - 6 - 7 - 8 - 9 - 10
复制代码




三、方法语法的运算


1. Select:指定对象的哪部分被选择(可以是整个数据项,也可以是其中的一个或几个字段),并将结果序列合并为一个序列

        //指定选择name字段,返回值类型var实际为IEnumerable<string>        var value21 = array3.Select(item => item.name);        TestLog(value21);        // - p01 - p02 - p03 - p04 - p05 - p06 - p07 - p08 - p09 - p10
复制代码


2. Where:对序列的元素进行筛选

        //对array1的元素进行筛选,只取level <= 5的元素        var value22 = array1.Where(item => item < 5);        TestLog(value22);        // - 0 - 1 - 2 - 3 - 4
复制代码


3. Join:联结两个序列,作为一个新的集合

        //以array1和array2作为一个新的集合,并取其中array1元素中和array2元素中level相等的元素        var value23 = array3.Join(array4, item1 => item1.level, item2 => item2.level, (item1, item2) => item1.level);        TestLog(value23);        // - 6 - 7 - 8 - 9 - 10
var value24 = array3.Join(array4, item1 => item1.level, item2 => item2.level, (item1, item2) => new { item1.level, playerName = item1.name, enemyName = item2.name }); TestLog(value24); // - { level = 6, playerName = p06, enemyName = e01 } - { level = 7, playerName = p07, enemyName = e02 } - { level = 8, playerName = p08, enemyName = e03 } - { level = 9, playerName = p09, enemyName = e04 } - { level = 10, playerName = p10, enemyName = e05 }
复制代码


4. SelectMany:将序列的每个元素投影到 IEnumerable<T> 并将结果序列合并为一个序列

        //筛选array3中的players字段,将每个players值拆散成IEnumerable<Player>,并将所有的IEnumerable<Player>合并为一个序列        var value25 = array5.SelectMany(group => group.players);        TestLog(value25);        // - Player - Player - Player - Player        var value26 = array5.SelectMany(group => group.players, (group, player) => player.name);        TestLog(value26);        // - pm11 - pm12 - pm21 - pm22
复制代码


5. GroupJoin:基于键值等同性对两个序列的元素进行关联,并对结果进行分组。 使用默认的相等比较器对键进行比较

        var value27 = array5.GroupJoin(array6, group01 => group01.players.Select(item => item.level), group02 => group02.enemies.Select(item => item.level), (group, groups) => group.players.Select(p => p.name));        foreach (var item in value27)        {            TestLog(item);            // - pm11 - pm12            // - pm21 - pm22        }
复制代码


6. Take:返回序列中的前 n 个对象

        var value28 = array1.Take(3);        TestLog(value28);        // - 0 - 1 - 2
复制代码


7. Skip:跳过序列中的前 n 个对象

        //Skip:跳过序列中的前n个对象        var value29 = array1.Skip(3);        TestLog(value29);        // - 3 - 4
复制代码


8. TakeWhile:迭代序列,如果当前项计算结果返回 true,就选择该项。在第一次返回 false 的时候,该项和其余项都被丢弃,不在判断后面的项

        //0返回true,1返回false,所以1及其后面的元素都丢弃        var value30 = array1.TakeWhile(i => i % 2 == 0);        TestLog(value30);        // - 0
复制代码


9.SkipWhile:迭代序列,如果当前项的计算结果返回 true,就跳过该项。在第一次返回 false 的时候,该项和其余项都被选择

        //SkipWhile:迭代序列,如果当前项的计算结果返回true,就跳过该项。在第一次返回false的时候,该项和其余项都被选择        var value31 = array1.SkipWhile(i => i % 2 == 0);        TestLog(value31);        // - 1 - 2 - 3 - 4
复制代码


10. Concat:连接两个序列

        var value32 = array2.Concat(array1);        TestLog(value32);        // - 5 - 6 - 7 - 8 - 9 - 0 - 1 - 2 - 3 - 4
复制代码


11. OrderBy:根据一个或多个键对序列中的元素排序

        var value33 = value32.OrderBy(i => i);        TestLog(value33);        // - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9
复制代码


12.  Reverse:反转序列中的元素

        var value34 = array1.Reverse();        TestLog(value34);        // - 4 - 3 - 2 - 1 - 0
复制代码


13. GroupBy:对序列中的元素分组

        var value35 = array1.GroupBy(i => i < 3);        foreach (IGrouping<bool, int> item in value35)        {            Debug.Log(item.Key);            TestLog(item);        }        //True        // - 0 - 1 - 2        //False        // - 3 - 4
复制代码


14. Distinct:去除序列中的重复项

        var value36 = array7.Distinct();        TestLog(value36);        // - 1 - 2 - 3 - 4 - 5 - 6
复制代码


15. Union:返回两个序列的并集

        var value37 = array2.Union(array7);        TestLog(value37);        // - 5 - 6 - 7 - 8 - 9 - 1 - 2 - 3 - 4
复制代码


16. Intersect:返回两个序列的交集

        var value38 = array1.Intersect(array7);        TestLog(value38);        // - 1 - 2 - 3 - 4
复制代码


17. Except:返回第一个序列中不重复的元素减去同样位于第二个序列中的元素

        var value39 = array7.Intersect(array1);        TestLog(value39);        // - 1 - 2 - 3 - 4
复制代码


18. AsEnumerable:将序列作为 IEnumerable<TSource>返回

        var value40 = array1.AsEnumerable();        Debug.Log(value40.GetType());
复制代码


19.ToArray:将序列作为数组返回

        var value41 = array1.ToArray();        Debug.Log(value41.GetType());      //System.Int32[]
复制代码


20. ToList:将序列作为 List<T>返回

        var value42 = array1.ToList();        Debug.Log(value42.GetType());       //System.Collections.Generic.List`1[System.Int32]
复制代码


21. ToDictionaty:将序列作为 Dictionary<TKey,TElement>

        var value43 = array1.ToDictionary(i => i);        Debug.Log(value43.GetType());   //System.Collections.Generic.Dictionary`2[System.Int32,System.Int32]
复制代码


22. ToLookup:将序列作为 LookUp<TKey,TElement>

        var value44 = array1.ToLookup(i => i);        Debug.Log(value44.GetType());       //System.Linq.Lookup`2[System.Int32,System.Int32]
复制代码


23. OfType:所返回的序列中元素是指定类型的元素

        var value45 = array1.OfType<int>();        TestLog(value45);        // - 0 - 1 - 2 - 3 - 4
复制代码


24. Cast:将序列中所有的元素强制转换为给定的类型

        var value46 = array1.Cast<string>();        Debug.Log(value46.GetType());   //System.Linq.Enumerable+<CastIterator>d__34`1[System.String]
复制代码


25. SequenceEqual:返回一个布尔值,指定两个序列是否相等

        var value47 = array1.SequenceEqual(array2);        Debug.Log(value47);         //False
复制代码


26.1 First:返回序列中第一个匹配的元素。如果没有元素匹配,抛出异常。如果没有给出条件,返回序列的第一个元素


26.2 FirstOrDefault:返回序列中第一个匹配的元素。如果没有元素匹配,返回该类型的默认值。如果没有给出条件,返回序列的第一个元素

        var value48 = array1.First();        Debug.Log(value48);         //0        value48 = array1.First(i => i > 2);        Debug.Log(value48);         //3        value48 = array1.FirstOrDefault(i => i > 10);        Debug.Log(value48);         //0
复制代码


27.1 Last:返回序列中最后一个匹配的元素。如果没有元素匹配,抛出异常。如果没有给出条件,返回序列的最后一个元素


27.2LastOrDefault:返回序列中最后一个匹配的元素。如果没有元素匹配,就返回默认值。如果没有给出条件,返回序列的最后一个元素

        //同上
复制代码


28.1 Single:返回序列中匹配的单个元素。如果没有元素匹配,或多于一个元素匹配,抛出异常


28.2 SingleOrDefault:返回序列中匹配的单个元素。如果没有元素匹配,返回默认值。如果多于一个元素匹配,抛出异常

        var value49 = array7.Single(i => i == 1);        Debug.Log(value49);             //1        //value49 = array7.Single(i => i == 2);        //Debug.Log(value49);           //报错(InvalidOperationException: Sequence contains more than one matching element)
value49 = array7.SingleOrDefault(i => i == 10); Debug.Log(value49); //0 //value49 = array7.SingleOrDefault(i => i == 2); //Debug.Log(value49); //报错(InvalidOperationException: Sequence contains more than one matching element)
复制代码


29.1 ElementAt:返回序列中指定索引处的元素;如果索引超出范围,抛出异常


29.2 ElementAtOrDefault:返回序列中指定索引处的元素;如果索引超出范围,返回默认值

        var value50 = array1.ElementAt(1);        Debug.Log(value50);             //1
var value51 = array1.ElementAtOrDefault(10); Debug.Log(value51); //0
复制代码


30. DefaultIfEmpty:返回指定序列中的元素;如果序列为空,则返回集合中类型参数的默认值

        List<int> array8 = new List<int>();        var value52 = array8.DefaultIfEmpty();        TestLog(value52);               // - 0
复制代码


31. Range:生成指定范围内的整数的序列。给定一个 start 整型和 count 整型,返回序列包含 count 个整型,其中第一个元素的值为 start,之后每个后续元素都比前一个大 1\

        var value53 = Enumerable.Range(10, 3);        TestLog(value53);               // - 10 - 11 - 12
复制代码


32. Repeat:生成包含一个重复值的序列

        var value54 = Enumerable.Repeat(10, 3);        TestLog(value54);               // - 10 - 10 - 10
复制代码


33. Empty:返回给定类型 T 的空序列

        var value55 = Enumerable.Empty<int>();        Debug.Log(value55.Count());     //0
复制代码


34. Contains:序列中是否包含给定的元素

        var value56 = array1.Contains(1);        Debug.Log(value56);     //True
复制代码


35.1 Count:返回序列中元素的个数(int)。重载可以添加筛选条件,返回满足条件的元素个数


35.2 LongCount:返回序列中元素的个数(long)。重载可以添加筛选条件,返回满足条件的元素个数

        var value57 = array1.Count();        Debug.Log(value57);     //5
var value58 = array1.LongCount(i => i < 3); Debug.Log(value58); //3
复制代码


36. Sum:返回序列中值的总和

        var value59 = array1.Sum();        Debug.Log(value59);     //10
复制代码


37.1 Min:返回序列中最小的值

37.2 Max:返回序列中最大的值

        var value60 = array1.Min();        Debug.Log(value60);     //0
var value61 = array1.Max(); Debug.Log(value61); //4
复制代码


38. Average:返回序列中的平均值

        var value62 = array1.Average();        Debug.Log(value62);     //2
复制代码


39. Any:序列中是否存在满足条件的元素

        var value63 = array1.Any(i => i > 0);        Debug.Log(value63);     //True
复制代码


40. All:序列中是否全部元素都满足条件

        var value64 = array1.All(i => i > 0);        Debug.Log(value64);     //False
复制代码


41. Append:将一个值追加到序列末尾

        var value65 = array1.Append(10);        TestLog(value65);     // - 0 - 1 - 2 - 3 - 4 - 10
复制代码


42. Aggregate:连续对序列中的各个元素应用给定的函数

        //Aggregate 01        //对序列应用累加器函数  将指定的种子值用作累加器初始值  并使用指定的函数选择结果值        //public static TResult Aggregate<TSource, TAccumulate, TResult>(this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func, Func<TAccumulate, TResult> resultSelector);
//Aggregate 02 //对序列应用累加器函数 将指定的种子值用作累加器初始值 //public static TAccumulate Aggregate<TSource, TAccumulate>(this IEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> func);
//Aggregate 03 //对序列应用累加器函数 //public static TSource Aggregate<TSource>(this IEnumerable<TSource> source, Func<TSource, TSource, TSource> func);
//TSource : source的元素类型 //TAccumulate : 累加器值的类型 //TResult : 结果值的类型
//source IEnumerable<TSource> : 要应用累加器的序列IEnumerable<T> //seed TAccumulate : 累加器的初始值 //func Func<TAccumulate, TSource, TAccumulate> : 要对每个元素调用的累加器函数 //resultSelector Func<TAccumulate, TResult> : 将累加器的最终值转换为结果值的函数
string[] fruits = { "apple", "mango", "orange", "passionfruit", "grape" };
//Aggregate 01 Demo 查找长度最长的字符串 string longestName = fruits.Aggregate("banana", (longest, next) => next.Length > longest.Length ? next : longest, fruit => fruit.ToUpper()); Debug.Log("longestName : " + longestName);
//Aggregate 02 Demo 查找名字以"e"结尾的的数量 int count = fruits.Aggregate(0, (total, next) => next.EndsWith("e") ? total + 1 : total); Debug.Log("count : " + count);
//Aggregate 03 Demo 颠倒字符串顺序 string sentence = "the quick brown fox jumps over the lazy dog"; // Split the string into individual words. string[] words = sentence.Split(' '); // Prepend each word to the beginning of the new sentence to reverse the word order. string reversed = words.Aggregate((workingSentence, next) => next + " " + workingSentence); Debug.Log("reversed : " + reversed);
//累加字典Dictionary的Value Dictionary<string, int> numDic = new Dictionary<string, int>(); int totalNum = numDic.Aggregate(0, (total, next) => total += next.Value);
复制代码


发布于: 刚刚阅读数: 3
用户头像

萧然🐳

关注

喜欢玩游戏,更喜欢做游戏 2020.03.26 加入

Unity游戏开发者

评论

发布
暂无评论
【C#】System.Linq,万能的查询语句_C#_萧然🐳_InfoQ写作社区