没有网站怎么做cpa广告,保靖网站建设,wordpress页面修改插件,wordpress文章竖线LINQ#xff08;Language Integrated Query#xff0c;语言集成查询#xff09;提供了类似于SQL的语法#xff0c;能对集合进行遍历、筛选和投影。一旦掌握了LINQ#xff0c;你就会发现在开发中再也离不开它。 开始!前言C#中的集合表现为数组和若干集合类。不管是数组还… LINQLanguage Integrated Query语言集成查询提供了类似于SQL的语法能对集合进行遍历、筛选和投影。一旦掌握了LINQ你就会发现在开发中再也离不开它。 开始!前言 C#中的集合表现为数组和若干集合类。不管是数组还是集合类它们都有各自的优缺点。如何使用好集合是我们在开发过程中必须掌握的技巧。不要小看这些技巧一旦在开发中使用了错误的集合或针对集合的方法应用程序将会背离你的预想而运行。正文1.元素数量可变的情况下不应使用数组 在C#中数组一旦被创建长度就不能改变。如果我们需要一个动态且可变长度的集合就应该使用ArrayList或ListT来创建。而数组本身尤其是一维数组在遇到要求高效率的算法时则会专门被优化以提升其效率。一维数组也称为向量其性能是最佳的在IL中使用了专门的指令来处理它们如newarr、ldelem、ldelema、ldlen和stelem。 从内存使用的角度来讲数组在创建时被分配了一段固定长度的内存。如果数组的元素是值类型则每个元素的长度等于相应的值类型的长度如果数组的元素是引用类型则每个元素的长度为该引用类型的IntPtr.Size。数组的存储结构一旦被分配就不能再变化。而ArrayList是数组结构可以动态地增减内存空间如果ArrayList存储的是值类型则会为每个元素增加12字节的空间其中4字节用于对象引用8字节是元素装箱时引入的对象头。ListT是ArrayList的泛型实现它省去了拆箱和装箱带来的开销。注意 由于数组本身在内存上的特点因此在使用数组的过程中还应该注意大对象的问题。所谓“大对象”是指那些占用内存超过85 000字节的对象它们被分配在大对象堆里。大对象的分配和回收与小对象相比都不太一样尤其是回收大对象在回收过程中会带来效率很低的问题。所以不能肆意对数组指定过大的长度这会让数组成为一个大对象。如果一定要动态改变数组的长度一种方法是将数组转换为ArrayList或ListT需要扩容时内部数组将自动翻倍扩容还有一种方法是用数组的复制功能。数组继承自System.Array抽象类System.Array提供了一些有用的实现方法其中就包含了Copy方法它负责将一个数组的内容复制到另外一个数组中。无论是哪种方法改变数组长度就相当于重新创建了一个数组对象。2.多数情况下使用foreach进行循环遍历采用foreach最大限度地简化了代码。它用于遍历一个继承了IEmuerable或IEmuerableT接口的集合元素。借助于IL代码可以看到foreach还是本质就是利用了迭代器来进行集合遍历。如下CopyListobjectlistnew Listobject(); using(Listobject.Enumerator CS$5$0000list.GetEnumerator()) { while(CS$5$0000.MoveNext()) { object currentCS$5$0000.Current; }}除了代码简洁之外foreach还有两个优势自动将代码置入try-finally块若类型实现了IDispose接口它会在循环结束后自动调用Dispose方法。3.foreach不能代替forforeach存在的一个问题是它不支持循环时对集合进行增删操作。 取而代之的方法是使用for循环。不支持原因:foreach循环使用了迭代器进行集合的遍历它在FCL提供的迭代器内部维护了一个对集合版本的控制。那么什么是集合版本简单来说其实它就是一个整型的变量任何对集合的增删操作都会使版本号加1。foreach循环会调用MoveNext方法来遍历元素在MoveNext方法内部会进行版本号的检测一旦检测到版本号有变动就会抛出InvalidOperationException异常。如果使用for循环就不会带来这样的问题。for直接使用索引器它不对集合版本号进行判断所以不存在因为集合的变动而带来的异常当然超出索引长度这种情况除外。Copypublic bool MoveNext(){ ListTlistthis.list; if((this.versionlist._version)(this.indexlist._size)) { this.currentlist._items[this.index]; this.index; return true; } return this.MoveNextRare();}无论是for循环还是foreach循环内部都是对该数组的访问而迭代器仅仅是多进行了一次版本检测。事实上在循环内部两者生成的IL代码也是差不多的。4.使用更有效的对象和集合初始化举例Copyclass Program { static void Main(string[]args) { Person personnew Person(){NameMike,Age20}; }}class Person{ public string Name{get;set;} public int Age{get;set;}}对象初始化设定项支持在大括号中对自动实现的属性进行赋值。以往只能依靠构造方法传值进去或者在对象构造完毕后对属性进行赋值。现在这些步骤简化了初始化设定项实际相当于编译器在对象生成后对属性进行了赋值。集合初始化也同样进行了简化:CopyListPersonpersonListnew ListPerson( ){ new Person() {NameRose,Age19}, mike, null};重点初始化设定项绝不仅仅是为了对象和集合初始化的方便它更重要的作用是为LINQ查询中的匿名类型进行属性的初始化。由于LINQ查询返回的集合中匿名类型的属性都是只读的如果需要为匿名类型属性赋值或者增加属性只能通过初始化设定项来进行。初始化设定项还能为属性使用表达式。举例CopyListPersonpersonList2new ListPerson(){ new Person(){NameRose,Age19}, new Person(){NameSteve,Age45}, new Person(){NameJessica,Age20}};var pTempfrom p in personList2 select new {p.Name, AgeScopep.Age20?Old:Young};foreach(var item in pTemp) { Console.WriteLine(string.Format({0}:{1},item.Name,item.AgeScope));}5.使用泛型集合代替非泛型集合注意非泛型集合在System.Collections命名空间下对应的泛型集合则在System.Collections.Generic命名空间下。泛型的好处不言而喻如果对大型集合进行循环访问、转型或拆箱和装箱操作使用ArrayList这样的传统集合对效率的影响会非常大。鉴于此微软提供了对泛型的支持。泛型使用一对括号将实际的类型括起来然后编译器和运行时会完成剩余的工作。6.选择正确的集合要选择正确的集合首先需要了解一些数据结构的知识。所谓数据结构就是相互之间存在一种或多种特定关系的数据元素的集合说明直接存储结构的优点是向数据结构中添加元素是很高效的直接放在数据末尾的第一个空位上就可以了。它的缺点是向集合插入元素将会变得低效它需要给插入的元素腾出位置并顺序移动后面的元素。如果集合的数目固定并且不涉及转型使用数组效率高否则就使用ListT(该使用数组的时候,还是要使用数组)顺序存储结构即线性表。线性表可动态地扩大和缩小它在一片连续的区域中存储数据元素。线性表不能按照索引进行查找它是通过对地址的引用来搜索元素的为了找到某个元素它必须遍历所有元素直到找到对应的元素为止。所以线性表的优点是插入和删除数据效率高缺点是查找的效率相对来说低一些。队列QueueT遵循的是先入先出的模式它在集合末尾添加元素在集合的起始位置删除元素。栈StackT遵循的是后入先出的模式它在集合末尾添加元素同时也在集合末尾删除元素。字典DictionaryTKey, TValue存储的是键值对值在基于键的散列码的基础上进行存储。字典类对象由包含集合元素的存储桶组成每一个存储桶与基于该元素的键的哈希值关联。如果需要根据键进行值的查找使用DictionaryTKey, TValue将会使搜索和检索更快捷。双向链表LinkedListT是一个类型为LinkedListNode的元素对象的集合。当我们觉得在集合中插入和删除数据很慢时就可以考虑使用链表。如果使用LinkedListT我们会发现此类型并没有其他集合普遍具有的Add方法取而代之的是AddAfter、AddBefore、AddFirst、AddLast等方法。双向链表中的每个节点都向前指向Previous节点向后指向Next节点。在FCL中非线性集合实现得不多。非线性集合分为层次集合和组集合。层次集合如树在FCL中没有实现。组集合又分为集和图集在FCL中实现为HashSetT而图在FCL中也没有对应的实现。集的概念本意是指存放在集合中的元素是无序的且不能重复的。除了上面提到的集合类型外还有其他几个要掌握的集合类型它们是在实际应用中发展而来的对以上基础类型的扩展SortedListT、SortedDictionaryTKey, TValue、Sorted-SetT。它们所扩展的对应类分别为ListT、DictionaryTKey, TValue、HashSetT作用是将原本无序排列的元素变为有序排列。除了排序上的需求增加了上面3个集合类外在命名空间System.Collections.Concurrent下还涉及几个多线程集合类。它们主要是ConcurrentBagT对应ListTConcurrentDictionaryTKey, TValue对应DictionaryTKey, TValueConcurrentQueueT对应QueueTConcurrentStackT对应StackTFCL集合图如下7.确保集合的线程安全集合线程安全是指在多个线程上添加或删除元素时线程之间必须保持同步。泛型集合一般通过加锁来进行安全锁定如下Copystatic object sycObjnew object();static void Main(string[]args){Thread t1new Thread((){autoSet.WaitOne();lock(sycObj){ foreach(Person item in list) { Console.WriteLine(t1:item.Name); Thread.Sleep(1000); }}}8.避免将ListT作为自定义集合类的基类如果要实现一个自定义的集合类不应该以一个FCL集合类为基类而应该扩展相应的泛型接口。FCL集合类应该以组合的形式包含至自定义的集合类需扩展的泛型接口通常是IEnumer-ableT和ICollectionT或ICollectionT的子接口如IListT前者规范了集合类的迭代功能后者则规范了一个集合通常会有的操作。ListT基本上没有提供可供子类使用的protected成员从object中继承来的Finalize方法和Member-wiseClone方法除外也就是说实际上继承ListT并没有带来任何继承上的优势反而丧失了面向接口编程带来的灵活性。而且稍加不注意隐含的Bug就会接踵而至。9.迭代器应该是只读的FCL中的迭代器只有GetEnumerator方法没有SetEnumerator方法。所有的集合类也没有一个可写的迭代器属性。原因有二这违背了设计模式中的开闭原则。被设置到集合中的迭代器可能会直接导致集合的行为发生异常或变动。一旦确实需要新的迭代需求完全可以创建一个新的迭代器来满足需求而不是为集合设置该迭代器因为这样做会直接导致使用到该集合对象的其他迭代场景发生不可知的行为。现在我们有了LINQ。使用LINQ可以不用创建任何新的类型就能满足任何的迭代需求。10.谨慎集合属性的可写操作如果类型的属性中有集合属性那么应该保证属性对象是由类型本身产生的。如果将属性设置为可写则会增加抛出异常的几率。一般情况下如果集合属性没有值则它返回的Count等于0而不是集合属性的值为null。11.使用匿名类型存储LINQ查询结果最佳搭档从.NET 3.0开始C#开始支持一个新特性匿名类型。匿名类型由var、赋值运算符和一个非空初始值或以new开头的初始化项组成。匿名类型有如下的基本特性既支持简单类型也支持复杂类型。简单类型必须是一个非空初始值复杂类型则是一个以new开头的初始化项匿名类型的属性是只读的没有属性设置器它一旦被初始化就不可更改如果两个匿名类型的属性值相同那么就认为两个匿名类型相等匿名类型可以在循环中用作初始化器匿名类型支持智能感知还有一点虽然不常用但是匿名类型确实也可以拥有方法。11. 在查询中使用Lambda表达式LINQ实际上是基于扩展方法和Lambda表达式的理解了这一点就不难理解LINQ。任何LINQ查询都能通过调用扩展方法的方式来替代如下面的代码所示Copyforeach(var item in personList.Select(personnew{PersonName person.Name,CompanyNameperson.CompanyID0?Micro:Sun})){ Console.WriteLine(string.Format({0}\t:{1},item.PersonName, item.CompanyName));}针对LINQ设计的扩展方法大多应用了泛型委托。System命名空间定义了泛型委托Action、Func和Predicate。可以这样理解这三个委托Action用于执行一个操作所以它没有返回值Func用于执行一个操作并返回一个值Predicate用于定义一组条件并判断参数是否符合条件。Select扩展方法接收的就是一个Func委托而Lambda表达式其实就是一个简洁的委托运算符“”左边代表的是方法的参数右边的是方法体。12.理解延迟求值和主动求值之间的区别样例如下CopyListintlistnew Listint(){0,1,2,3,4,5,6,7,8,9};var temp1from c in list where c5 select c;var temp2(from c in list where c5 select c).ToListint();在使用LINQ to SQL时延迟求值能够带来显著的性能提升。举个例子如果定义了两个查询而且采用延迟求值CLR则会合并两次查询并生成一个最终的查询。13.区别LINQ查询中的IEnumerableT和IQueryableTLINQ查询方法一共提供了两类扩展方法在System.Linq命名空间下有两个静态类Enumerable类它针对继承了IEnumerableT接口的集合类进行扩展Queryable类它针对继承了IQueryableT接口的集合类进行扩展。稍加观察我们会发现接口IQueryableT实际也是继承了IEnumerableT接口的所以致使这两个接口的方法在很大程度上是一致的。那么微软为什么要设计出两套扩展方法呢我们知道LINQ查询从功能上来讲实际上可分为三类LINQ to OBJECTS、LINQ to SQL、LINQ to XML本建议不讨论。设计两套接口的原因正是为了区别对待LINQ to OBJECTS、LINQ to SQL两者对于查询的处理在内部使用的是完全不同的机制。针对LINQ to OBJECTS时使用Enumerable中的扩展方法对本地集合进行排序和查询等操作查询参数接受的是Func。Func叫做谓语表达式相当于一个委托。针对LINQ toSQL时则使用Queryable中的扩展方法它接受的参数是Ex-pression。Expression用于包装Func。LINQ to SQL引擎最终会将表达式树转化成为相应的SQL语句然后在数据库中执行。那么到底什么时候使用IQueryableT什么时候使用IEnumerableT呢简单表述就是本地数据源用IEnumer-ableT远程数据源用IQueryableT。注意在使用IQueryableT和IEnumerableT的时候还需要注意一点IEnumerableT查询的逻辑可以直接用我们自己所定义的方法而IQueryableT则不能使用自定义的方法它必须先生成表达式树查询由LINQ to SQL引擎处理。在使用IQueryableT查询的时候如果使用自定义的方法则会抛出异常。13.使用LINQ取代集合中的比较器和迭代器LINQ提供了类似于SQL的语法来实现遍历、筛选与投影集合的功能。借助于LINQ的强大功能我们通过两条语句就能实现上述的排序要求。Copyvar orderByBonusfrom s in companySalary orderby s.Bonus select s;foreach实际会隐含调用的是集合对象的迭代器。以往如果我们要绕开集合的Sort方法对集合元素按照一定的顺序进行迭代则需要让类型继承IEnumerable接口泛型集合是IEnumerableT接口实现一个或多个迭代器。现在从LINQ查询生成匿名类型来看相当于可以无限为集合增加迭代需求。有了LINQ之后我们是否就不再需要比较器和迭代器了呢答案是否定的。我们可以利用LINQ的强大功能简化自己的编码但是LINQ功能的实现本身就是借助于FCL泛型集合的比较器、迭代器、索引器的。LINQ相当于封装了这些功能让我们使用起来更加方便。在命名空间Sys-tem.Linq下存在很多静态类这些静态类存在的意义就是为FCL的泛型集合提供扩展方法强烈建议你利用LINQ所带来的便捷性但我们仍需掌握比较器、迭代器、索引器的原理以便更好地理解LINQ的思想写出更高质量的代码。最好是能看懂Linq源码。Copypublic static IOrderedEnumerableTSourceOrderByTSource,TKey(this IEnumerableTSourcesource,FuncTSource,TKeykeySelector){ //省略}14.在LINQ查询中避免不必要的迭代比如常使用First()方法First方法实际完成的工作是搜索到满足条件的第一个元素就从集合中返回。如果没有符合条件的元素它也会遍历整个集合。与First方法类似的还有Take方法Take方法接收一个整型参数然后为我们返回该参数指定的元素个数。与First一样它在满足条件以后会从当前的迭代过程直接返回而不是等到整个迭代过程完毕再返回。如果一个集合包含了很多的元素那么这种查询会为我们带来可观的时间效率。会运用First和Take等方法都会让我们避免全集扫描大大提高效率。总结如有需要 上一篇的《c#规范整理·语言要素》也可以看看原文地址https://www.cnblogs.com/zhan520g/p/11018163.html.NET社区新闻深度好文欢迎访问公众号文章汇总 http://www.csharpkit.com