Linq标准查询操作符
Linq的出现让代码简洁了不少。之前在项目中基本都在使用它,但是没有完整的整理过,今天借这个周末,将其进行整理,方便后期对其的使用。Linq的操作可以分为聚合,连接,转换,元素操作符,相等操作,生成,分组,分部,投影,数量,过滤,排序等,下面我们就通过实例的方式一一说下。
在具体实例之前先定义两个集合供下面的使用和封装的三个控制台输出方法:
1 List<string> words = new List<string>() { "zero", "one", "two", "three", "four" }; 2 List<int> numbers = new List<int>() { 0, 1, 2, 3, 4 };
1 static void Write(string name, object obj) 2 { 3 Console.WriteLine($"method:{name},\t result:{obj.ToString()}"); 4 } 5 6 static void Write(string title) 7 { 8 Console.WriteLine($"---------{title}-------------"); 9 } 10 11 static void Write() 12 { 13 Console.WriteLine($"-------------------------------"); 14 }
聚合
最常见的聚合操作符就是Aggregate,所有的其他聚合操作符都能表示为对Aggregate的调用,所有聚合操作符都使用立即执行的模式
1 Write("聚合操作"); 2 int sum = numbers.Sum(); 3 Write("sum", sum); 4 int count = numbers.Count; 5 Write("count", count); 6 double average = numbers.Average(); 7 Write("average", average); 8 long longCount = numbers.LongCount(x => x % 2 == 0); 9 Write("longCount", longCount); 10 int min = words.Min(p => p.Length); 11 Write("min", min); 12 int max = words.Max(p => p.Length); 13 Write("max", max); 14 string aggregate = numbers.Aggregate("seed", (current, item) => current + item, result => result.ToUpper()); 15 Write("aggregate", aggregate); 16 Write();
结果:
连接
1 Write("连接"); 2 List<int> concat = numbers.Concat(new List<int>() { 2, 3, 4, 5, 6 }).ToList(); 3 Write("concat", string.Join(",", concat)); 4 Write();
结果:
转换
toArray和toList的含义显而易见,他们读取整个序列到内存中,并把结果作为一个数组或一个List<T>返回。两者都是立即执行,cast和ofType把一个非类型化序列转换为类型化序列的,或抛出异常(对于cast),或忽略那些不能隐式转换为序列元素类型的输入序列元素(对于ofType),这个运算符也能用于把某个类型化序列转换为更加具体的类型化序列。转换以流的形式延迟执行。ToDictionary和TolookUp都使用委托来获得任何特定元素的键。执行模式为立即执行
1 Write("转换"); 2 List<Object> allString = new List<Object>() { "These", "are", "all", "strings" }; 3 List<Object> notAllString = new List<Object>() { "Number", "at", "the", "end", 5 }; 4 5 IEnumerable<string> castOne = allString.Cast<string>(); 6 Write("castOne", string.Join(",", castOne)); 7 IEnumerable<string> ofTypeOne = allString.OfType<string>(); 8 Write("ofTypeOne", string.Join(",", ofTypeOne)); 9 10 IEnumerable<string> castTwo = notAllString.Cast<string>(); 11 Write("castOne", string.Join(",", castTwo));//遍历时遇到转换失败的地方,会抛出异常 12 IEnumerable<string> ofTypeTwo = notAllString.OfType<string>(); 13 Write("ofTypeOne", string.Join(",", ofTypeTwo)); 14 15 int[] toArray = numbers.ToArray(); 16 List<int> toList = numbers.ToList(); 17 18 Dictionary<string, string> dictionary = words.ToDictionary(w => w.Substring(0, 2)); 19 ILookup<char, string> toLookUp = words.ToLookup(word => word[0]); 20 21 IDictionary<char, string> toDictionary = words.ToDictionary(p => p[0]);//异常:每个键只能有一个数据项,所以与遇到‘t’时转换会异常 22 23 Write();
元素操作符
每个操作符都以同样的方式执行,选取单个元素的简化版本,就是在特定元素存在时返回他,不存在时就抛出一个异常,还有一个以orDefault结尾的版本,orDefault版本的功能完全一样,只是在找不到想要的元素是,返回结果类型的默认值,而非抛出异常,但是有一种例外,如果序列为空,singleDefault将返回默认值,但是如果序列中的元素不止一个,将抛出异常,就像Single一样.。所有操作符都是使用立即执行模式
1 var elementAt = words.ElementAt(2);//"Two" 2 var elementAtOrDefault = words.ElementAtOrDefault(10);//Null 3 var first = words.First();//Zero 4 var firstTwo = words.First(p => p.Length == 3);//one 5 // var firstThree = words.First(p=>p.Length==10);//异常 6 var firstOrDefault = words.FirstOrDefault(p => p.Length == 10);//Null 7 var last = words.Last();//four 8 var single = words.Single();//异常,不止一个元素 9 var singleDefault = words.SingleOrDefault();//异常,不止一个元素 10 var singleTwo = words.Single(p => p.Length == 5);//three 11 var singleThree = words.Single(p => p.Length == 10);//异常,没有匹配的元素 12 var singleDefaultTwo = words.SingleOrDefault(P => P.Length == 10);//Nul
相等操作
按照顺序逐一比较两个序列中的元素是否相等,执行模式为立即执行
1 bool sequenceEqual = words.SequenceEqual(new List<string>() { "zero", "one", "two", "three", "four" });//true 2 bool sequenceEqualTwo = words.SequenceEqual(new List<string>() { "ZERO", "OME", "TWO", "THREE", "FOUR" });//false 3 bool sequenceEqualThree = words.SequenceEqual(new List<string>() { "ZERO", "OME", "TWO", "THREE", "FOUR" }, StringComparer.OrdinalIgnoreCase);//true
生成
在所有的生成操作符中,只有一个会对现有的序列进行处理:DefaultIfEmpty.如果序列不为空,就返回原始序列,否则返回含有但非我元素的序列。其中的元素通常是序列类型的默认值,不过重载方法允许你设定要使用的值.所有生成的操作符都是用延迟执行,并对结果进行流式处理。也就是说,他们不会预先生成集合并返回。不过,返回正确类型的空数组的empty方法是个例外。一个空的数组是完全不可变的,因此相同元素类型的所有元素类型的所有这种调用,都将返回相同的空数组。
1 var defaultIfEmpty = numbers.DefaultIfEmpty();//0,1,2,3,4 2 var defaultIfEmptyTwo = new int[0].DefaultIfEmpty();//0 3 var defaultIfEmptyThree = new int[0].DefaultIfEmpty(10);//0 4 var range = Enumerable.Range(15, 2);//15,16 5 var repeat = Enumerable.Repeat(25, 2);//25,25 6 IEnumerable<int> empty = Enumerable.Empty<int>();//一个类型为IEnumerable<int>的空序列
分组
1 IEnumerable<IGrouping<int, string>> groupby = words.GroupBy(p => p.Length); 2 IEnumerable<IGrouping<int, string>> groupTwo = words.GroupBy(p => p.Length, p => p.ToUpper()); 3 IEnumerable<string> groupThree = words.GroupBy(p => p.Length, (key, g) => key + ":" + g.Count());
结果:
投影
Select是一种简单的从源元素到结果元素的一对一投影,selectMany在查询表达式中有多个from子句的时候使用;原始序列中的每个元素都用来生成新的序列。两个投影都是
延迟执行,.net 4 引入了一个新的操作符zip。它包含两个序列,并对每个元素对应用指定的投影:先是每个序列的第一个元素,然后是每个序列的第二个元素,以此类推
任何一个源序列达到末尾时,结果序列都将停止产生。
1 IEnumerable<int> selectOne = words.Select(p => p.Length);//4,3,3,5,4 2 IEnumerable<string> selectTwo = words.Select((word, index) => index.ToString() + ":" + word);//0:zero,1:one .... 3 IEnumerable<char> selectMany = words.SelectMany(p => p.ToCharArray());//z,e,r,o,o... 4 IEnumerable<string> selectManyTwo = words.SelectMany((word, index) => Enumerable.Repeat(word, index)); 5 IEnumerable<string> zip = names.Zip(colors, (x, y) => x + "-" + y);
数量
数量操作符都返回一个boolean值,使用立即执行
1 bool allOne = words.All(p => p.Length > 3);//false 2 bool allTwo = words.All(p => p.Length > 2);//true 3 bool any = words.Any();//true 4 bool anyTwo = words.Any(p => p.Length == 6);//false 5 bool antThree = words.Any(p => p.Length == 5);//true 6 bool contains = words.Contains("FOUR");//false 7 bool containsTwo = words.Contains("FOUR", StringComparer.OrdinalIgnoreCase);//true
过滤
两个过滤操作符where和ofType。where返回一个序列,where总是使用延迟执行和流式数据。
1 IEnumerable<string> where = words.Where(p => p.Length > 3);//zero,three,four 2 IEnumerable<string> whereTwo = words.Where((word, index) => index < word.Length);//one,two,three
基于集的操作符
把两个序列作为元素的集合是很自然的。4个基于几何的运算符都具有两个重载方法,一个使用元素类型的默认相等比较,一个用于额外的参数中指定比较。所有的集合运算符都是延迟执行的
1 List<string> abbc = new List<string>() { "a", "b", "b", "c" }; 2 List<string> cd = new List<string>() { "c", "d" }; 3 4 var distinct = abbc.Distinct();//a,b,c 5 var intersect = abbc.Intersect(cd);//c 交集 6 var union = abbc.Union(cd);//a,b,c,d //并集 7 var expect = abbc.Except(cd);//a,b //差集 8 var exceptTwo = cd.Except(abbc);//d
排序
orderBy和OrderByDescending提供了主要的排序方式,thenBy 和ThenByDescending提供了次要排序方式。用以区别使用主要的排序方式无法区别的元素。在每种情况中,都要指定从元素到排序键的投影,也指定键之间的比较。linq排序比较稳定,即如果两个元素根据他们的排序关键字被认为相等,那么将按照他们原始序列中的顺序返回
1 var orderBy = words.OrderBy(p => p);//four,one,three,two,zero 2 var orderByTwo = words.OrderBy(p => p[1]);//zero,three,one,four,two 3 var orderByThree = words.OrderBy(p => p.Length);//one,two,zero,four,three 4 var orderByDescending = words.OrderByDescending(p => p.Length);//three,zero,four,one,two 5 var orderByFour = words.OrderBy(p => p.Length).ThenBy(p => p);//one,two,four,zero,three 6 var orderByFive = words.OrderBy(p => p.Length).ThenByDescending(p => p);//two,one,zero,four,three 7 words.Reverse();//four,three,two,one,zero