作为一个业余开发,断断续续学.net/c#也有不少日子了,
学习过程中,不断忘了学,学了忘,这让我很苦恼。
以前学习过程中,我总是在笔记本中记录下来知识要点,这么久下来,笔记本都写了四五本了。
然而,随着笔记本的增多,自己很快发现,笔记写了跟没写一样:笔记多了就找不到了……
所以,我觉得还是上博客园写博客,记录自己每天的学习心得。
如果有什么错误的地方,欢迎大神指教,小弟在这给大神跪谢了
============================================================
Linq入门篇
前篇:
一直以来我都没接触过EF,因为我觉得它涉及到linq等乱七八糟的东西,还不如用原生ado.net舒服。最近想开了,大胆得尝试EF,那么就向着原先最头疼的linq攻关。
我想很多人肯定跟我一样,第一次接触linq的时候,被linq,特别是它那些什么where 、order by子句弄得一脸懵逼。 我向来喜欢刨根寻底,对于那些只知其然不知其所以然的东西,我总是觉得浑身不舒服,这也是阻碍我学习linq的最大障碍。
问题在于:这个语法到底是怎么回事?这些子句到底有什么联系?
解释:
linq,书面解释就是Language Integrated Query,语言集成查询。单从字面上和百度百科上的解释,对于我等新手来说,看了恐怕只会更加懵逼,我想写这些的人他们是站在他们已经懂了的立场上写的解释,而不是我们这些不懂的人的立场。
(一)从新手角度来说,要弄懂linq,首先就从以前如何查询集合入手。 关键词:委托 ------------------------如果不知道委托是啥,很简单的一句话可以理解委托:它就是一个存方法的类型。你可以借助委托把方法当做变量传递。
我们假定有这样一个person类和 person类型 标识符为“persons”的泛型集合。
public class Person { public string Age{get;set;} public string Name{get;set;} public string Add{get;set;} } public class Program { public void Main() { List<Person> persons=new List<Person>(); persons.Add(new Person(){Age=10,Name="张三",Add="成都"}); persons.Add(new Person(){Age=13,Name="李四",Add="成都"}); persons.Add(new Person(){Age=17,Name="王五",Add="重庆"}); persons.Add(new Person(){Age=15,Name="路人",Add="香港"}); } }
这样一个list泛型集合中,我们要查询这个集合里面,所有对象的ADD属性为“成都”的对象。传统的查询办法之一就是调用 persons.FindAll(委托参数) 方法。
public void Main() { List<Person> persons=new List<Person>(); persons.Add(new Person(){Age=10,Name="张三",Add="成都"}); persons.Add(new Person(){Age=13,Name="李四",Add="成都"}); persons.Add(new Person(){Age=17,Name="王五",Add="重庆"}); persons.Add(new Person(){Age=15,Name="路人",Add="香港"}); /*chengduPerson是所有Add为成都的集合,找出persons集合中所有地址为成都的Person*/ List<Person> chengduPerson=persons.FindAll(p=>p.Add="成都"); }
我们用反编译工具点开FindAll()方法看看他们到底是怎么回事呢?
//List<>集合的FindAll的源码 [__DynamicallyInvokable] public List<T> FindAll(Predicate<T> match) //参数是一个泛型委托 这个委托要求方法传递一个集合元素类型的参数,返回一个BOOL值。具体说明在下面 { if (match == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match); } List<T> list = new List<T>(); for (int i = 0; i < this._size; i++) { if (match(this._items[i])) //借助match 来判断,传递过来的集合元素是否匹配,如果匹配就加入新的集合中。 { list.Add(this._items[i]); } } return list; } //参数 Predicate<T> match 的委托声明如下 namespace System { // 摘要: // 表示定义一组条件并确定指定对象是否符合这些条件的方法。 // // 参数: // obj: // 要按照由此委托表示的方法中定义的条件进行比较的对象。 // // 类型参数: // T: // 要比较的对象的类型。 // // 返回结果: // 如果 obj 符合由此委托表示的方法中定义的条件,则为 true;否则为 false。 public delegate bool Predicate<in T>(T obj); }
原来list集合就是借助委托来实现查询对象的。
(二)list集合只是.net中众多集合中的一种,List<>我们可以用FindAll,那其他集合呢?我们可不可以有什么办法让他们都能用一个通用的方法呢? 有! 关键词:扩展方法 和 IEnumerable<>
(1) 扩展方法是.net3.0的特性 它的具体语法就是在某个静态类的一个静态方法中第一个参数添加一个(this T name) T是期望添加到的类型。
public static class Test { public static void NewFunc(this string s) {………} } //将NewFunc添加到字符串对象中,字符串都可以调这个方法。扩展放方法其实并不是真正的给类型加方法,还是有区别的
(2)IEnumerable<>接口是实现遍历的接口,基本上如list<>、 Dictionary<>等泛型集合都实现了这个接口,与之对应的,非泛型版的集合则实现的是IEnumerable接口
明白这两点,我们就理解得出一个办法,我们要实现这样一个通用的集合查询。我们要实现使所有集合都可以通用一个方法进行查询,我们可以单独定义一个静态类,给IEnumerable、IEnumerable<>加扩展方法。使所有实现了 IEnumerable、IEnumerable<>的对象都可以调用这两个方法。
public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, int, TResult> selector) { }
借助扩展方法和扩展方法 和 IEnumerable接口就可以实现所有集合都能调取查询方法。
(三)重点来了----Linq 关键词 Enumerable 静态类。
事实上,微软就是这样做的,在namespace System.Linq命名空间下,有一个叫Enumerable的静态类,它定义了WHERE、SELECT、OrderBy等方法,他们都是扩展方法,全部扩展给IEnumerable;
[__DynamicallyInvokable] public static IEnumerable<TResult> Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, int, TResult> selector) { if (source == null) { throw Error.ArgumentNull("source"); } if (selector == null) { throw Error.ArgumentNull("selector"); } return SelectIterator<TSource, TResult>(source, selector); } private static IEnumerable<TResult> SelectIterator<TSource, TResult>(IEnumerable<TSource> source, Func<TSource, int, TResult> selector) { int iteratorVariable0 = -1; foreach (TSource iteratorVariable1 in source) { iteratorVariable0++; yield return selector(iteratorVariable1, iteratorVariable0); } } /*Enumerable 类中SELECT方法的源码*/ 需要注意,这里有个yield关键字,它的存在,使得这个查询会推迟到集合被遍历的时候才会执行
微软定义这样一个类,给我们提供了一个巨大的便利,这其实就是Linq的最核心的东西。 而诸如一些简写的子句 例如 from XXX in XXX where XXX.XXX>XXX select XXX 这些WHERE、SELECT子句,实际上就是对这些方法的映射 的语法糖。
如此一来,我们对Linq就有了一个最基本的了解。通过查看元数据,我们就可以知道我们可以对集合进行怎样的操作。为日后更深入的学习和应用打下良好的基础。
结语:
实现Linq,用到了委托、多态、扩展方法等C#和面向对象的特性。通过对Linq的学习,再次让我感受到了设计之美。真是受益匪浅。
参考:C#高级编程第11章 Linq