我们接着上篇Linq系列文章继续谈Linq的基石,上篇文章中,我们谈到了隐式类型局部变量以及对象集合初始化器,今天我们说说Lambda表达式、扩展方法和匿名类型。按计划,这篇文章昨天就应该写出来了,可是昨天确实很累,给自己定的目标是每天都写一篇技术文章,来总结每天学习的成果。又给自己的懒惰找借口了。。。
好吧,言归正传。对了,昨天马云好像辞去了阿里巴巴CEO一职,哎,羡慕啊,向他学习。希望能从他身上学点东西……
呵呵,又扯了,这次真的进入到主题。我们接着上篇文章中用到的实例,我们继续给这个实例进行进一步的优化。现在,我想给DisplayProcess方法添加一个过滤条件,输出占用内存空间大于20M的进程,首先我利用“硬编码”来实现过滤,其他代码不变主要是在DisplayProcess方法中添加一个if判断,好的,看代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// <summary> /// 给方法加上过滤条件,输出占用内存超过20M的进程 /// </summary> static void DisplayProcess() { var processes = new List<ProcessData>(); foreach (var process in Process.GetProcesses()) { //硬编码,添加过滤条件 if (process.WorkingSet64 >= 20 * 1024 * 1024) { processes.Add(new ProcessData { Id = process.Id, Name = process.ProcessName, Memory = process.WorkingSet64 }); } }
show结果:
上图列出的进程Memory大小都是计算机中分配的物理内存大于20M的进程。可是硬编码有局限性,假如我现在改变过滤条件,我希望输出占用内存大小超过30M的进程,那么我们又要到源代码中修改过滤条件,这样一来肯定很麻烦,所以我们想到一个解决办法,就是添加一个过滤器来替代硬编码,让我们写的程序更加的通用。
熟悉委托:
这里,我们的过滤器使用的是委托中的Predicate<T>委托,我们通常叫做“断言”,这个委托是通过我们输入的类型来返回true或false,我们可以根据它来实现过滤,我这样说大家可能不好理解,那我直接写代码,大家看完代码就能明白我说的是什么意思了。
修改DisplayProcess方法:把过滤器作为参数传进去
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// <summary> /// 将过滤条件作为参数传进去 /// </summary> /// <param name="match">过滤条件</param> static void DisplayProcess(Predicate<Process> match) { var processes = new List<ProcessData>(); foreach (var process in Process.GetProcesses()) { if (match(process)) { processes.Add(new ProcessData { Id = process.Id, Name = process.ProcessName, Memory = process.WorkingSet64 }); } } //遍历processes,输出到控制台 foreach (var data in processes) { Console.Write("Id = {0}\tName = {1}\tMemory = {2}", data.Id, data.Name, data.Memory); Console.WriteLine(); } }
增加过滤器方法:Filter
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// <summary> /// 过滤器 /// </summary> /// <param name="process">参数</param> /// <returns>返回值,true或false</returns> static Boolean Filter(Process process) { return process.WorkingSet64 >= 20 * 1024 * 1024; }
在Mian函数中调用DisplayProcess方法时将Filter传进去:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
static void Main(string[] args) { DisplayProcess(Filter); Console.ReadKey(); }
好的,我们看运行结果:
哈哈,同样实现了过滤,这样做的优势是,当我们想要改变过滤条件的时候,我们只要改变过滤器中的条件即可,而不用去动DisplayProcess,我们可以将条件写在App.config或Web.config配置文件中,这样修改起来不用重新编译源代码了。(我这里为了省事,没有将条件写在配置文件中。。。)
当然,我们也可以不写Filter方法,直接用匿名方法来代替,在Main函数中调用的时候,我们可以这样写:
static void Main(string[] args) { //DisplayProcess(Filter); DisplayProcess(delegate(Process process) { return process.WorkingSet64 >= 20 * 1024 * 1024; }); Console.ReadKey(); }
效果跟上面是一样的。
写了这里,大家是不是感觉到不管是Filter方法还是在Main函数中使用匿名方法,都比较麻烦;下面就进入到我们的主题:Lambda
重点一:Lambda表达式
我们只要在Main函数中调用Display方法的时候传入一个Lambda表达式即可,先给大家看一看:
static void Main(string[] args) { //DisplayProcess(Filter); #region 匿名方法 //DisplayProcess(delegate(Process process) // { // return process.WorkingSet64 >= 20 * 1024 * 1024; // }); #endregion //Lambda表达式 DisplayProcess(process => process.WorkingSet64 >= 20 * 1024 * 1024); Console.ReadKey(); }
哈哈,是不是很爽,同样能达到效果。这就是我们今天的第一个重点,Lambda表达式。有关于Lamda表达式的详细内容这里我就不再详细介绍了,大家可以去MSDN上学习,MSDN真的是学习的一个很好的资源,希望大家能够好好利用,另外在上文中我介绍到了委托,有关于委托的原理前段时间我写了一篇文章,大家可以去看看,我把链接帖上来。http://www.cnblogs.com/ARMdong/archive/2013/05/01/3053678.html,好,我们进入今天的第二个重点:扩展方法。
重点二:扩展方法
在开始扩展方法之前,我们引进一个小插曲,也就是在上面的实例中,我们想假如一个统计所有进程占用的内存总大小的方法。首先我们想到的就是添加一个统计的方法不就得了,好的,我根据大家的思维来写这样的一个方法:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// <summary> /// 统计所有进程占用内存大小的方法 /// </summary> /// <param name="processes">所有的进程集合</param> /// <returns>返回值,消耗内存空间</returns> static Int64 TotalMemory(IEnumerable<ProcessData> processes) { Int64 result = 0; foreach (var process in processes) { result += process.Memory; } return result; }
在DisplayProcess中调用TotalMemory方法:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// <summary> /// 将过滤条件作为参数传进去 /// </summary> /// <param name="match">过滤条件</param> static void DisplayProcess(Predicate<Process> match) { var processes = new List<ProcessData>(); foreach (var process in Process.GetProcesses()) { if (match(process)) { processes.Add(new ProcessData { Id = process.Id, Name = process.ProcessName, Memory = process.WorkingSet64 }); } } //遍历processes,输出到控制台 foreach (var data in processes) { Console.Write("Id = {0}\tName = {1}\tMemory = {2}", data.Id, data.Name, data.Memory); Console.WriteLine(); } Console.WriteLine(); //统计占用的内存空间 Console.WriteLine("消耗的内存总大小为:{0}M", TotalMemory(processes) / (1024 * 1024)); }
Main函数中列出所有进程,不添加过滤条件:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
static void Main(string[] args) { //DisplayProcess(process => process.WorkingSet64 >= 20 * 1024 * 1024); //这里我们就不再过滤了,列出所有的进程 DisplayProcess(process => process.WorkingSet64 > 0); Console.ReadKey(); }
我们看看运行结果:
打开任务管理器:
我的机器物理内存大小是4G,其实真正能用的只有3.8~3.9G,所有进程消耗的内存是1989M,所占百分比为51%,差不多。
讲到这里,大家可能在想,这跟扩展方法有毛的关系啊,是的,到目前为止,跟扩展方法还没有关系。下面我让它跟扩展方法有关系,我让他能在IEnumerable<ProcessData> processes的泛型集合processes中直接能点(.)出来,让他成为IEnumerable<ProcessData>自己的方法,像这样:processes.TotalMemory(),代码中能点是一种享受,你们觉得呢?好吧下面我开始小小的改动一下TotalMemory的方法:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// <summary> /// 为IEnumerable<ProcessData>泛型添加扩展方法 /// </summary> /// <param name="processes">所有的进程集合</param> /// <returns>返回值,消耗内存空间</returns> static Int64 TotalMemory(this IEnumerable<ProcessData> processes) { Int64 result = 0; foreach (var process in processes) { result += process.Memory; } return result; }
我在IEnumerable<ProcessData> processes参数前面加上一个this关键字,然后修改DisplayProcess中对TotalMemory的调用:
//统计占用的内存空间 //Console.WriteLine("消耗的内存总大小为:{0}M", TotalMemory(processes) / (1024 * 1024)); Console.WriteLine("消耗的内存总大小为:{0}M", processes.TotalMemory() / (1024 * 1024));
好了,运行一下我们的代码,结果和上面的一样,是不是很爽,在添加扩展方法的时候,我们要注意两点:首先是this关键字必须加载方法的第一个参数前面,这个参数的类型就是我们要扩展的类型。其次,扩展方法和其所属的类必须用static修饰:我把整个静态类代码贴出来。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//必须为静态类 static class Program { static void Main(string[] args) { //DisplayProcess(process => process.WorkingSet64 >= 20 * 1024 * 1024); //这里我们就不再过滤了,列出所有的进程 DisplayProcess(process => process.WorkingSet64 > 0); Console.ReadKey(); } /// <summary> /// 将过滤条件作为参数传进去 /// </summary> /// <param name="match">过滤条件</param> static void DisplayProcess(Predicate<Process> match) { var processes = new List<ProcessData>(); foreach (var process in Process.GetProcesses()) { if (match(process)) { processes.Add(new ProcessData { Id = process.Id, Name = process.ProcessName, Memory = process.WorkingSet64 }); } } //遍历processes,输出到控制台 foreach (var data in processes) { Console.Write("Id = {0}\tName = {1}\tMemory = {2}", data.Id, data.Name, data.Memory); Console.WriteLine(); } Console.WriteLine(); //统计占用的内存空间 //Console.WriteLine("消耗的内存总大小为:{0}M", TotalMemory(processes) / (1024 * 1024)); Console.WriteLine("消耗的内存总大小为:{0}M", processes.TotalMemory() / (1024 * 1024)); } /// <summary> /// 为IEnumerable<ProcessData>泛型添加扩展方法 /// </summary> /// <param name="processes">所有的进程集合</param> /// <returns>返回值,消耗内存空间</returns> static Int64 TotalMemory(this IEnumerable<ProcessData> processes) { Int64 result = 0; foreach (var process in processes) { result += process.Memory; } return result; } }
嗯,扩展方法就告一段落了,想要继续深入的话,自学,别忘了MSDN是一个自学的非常好的资源。下面就开始今天的第三个重点:匿名类型
重点三:匿名类型
这里为了方便,我对前面的例子做一个小小的改动,我们摈弃了过滤器的功能,还有并没有定义ProcessData这个实体类,同样我们完成输出所有的进程:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//展示进程的方法 static void DisplayProcess() { var processes = new List<Object>(); foreach (var process in Process.GetProcesses()) { //匿名类型 processes.Add(new { Id = process.Id, Name = process.ProcessName, Memory = process.WorkingSet64 }); } //控制台输出 ObjectDumper.Write(processes); }
这里用到了一个ObjectDumper的helper类,他的功能主要是在控制台显示数据:代码我贴在下面
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class ObjectDumper { public static void Write(object element) { Write(element, 0); } public static void Write(object element, int depth) { Write(element, depth, Console.Out); } public static void Write(object element, int depth, TextWriter log) { ObjectDumper dumper = new ObjectDumper(depth); dumper.writer = log; dumper.WriteObject(null, element); } TextWriter writer; int pos; int level; int depth; private ObjectDumper(int depth) { this.depth = depth; } private void Write(string s) { if (s != null) { writer.Write(s); pos += s.Length; } } private void WriteIndent() { for (int i = 0; i < level; i++) writer.Write(" "); } private void WriteLine() { writer.WriteLine(); pos = 0; } private void WriteTab() { Write(" "); while (pos % 8 != 0) Write(" "); } private void WriteObject(string prefix, object element) { if (element == null || element is ValueType || element is string) { WriteIndent(); Write(prefix); WriteValue(element); WriteLine(); } else { IEnumerable enumerableElement = element as IEnumerable; if (enumerableElement != null) { foreach (object item in enumerableElement) { if (item is IEnumerable && !(item is string)) { WriteIndent(); Write(prefix); Write("..."); WriteLine(); if (level < depth) { level++; WriteObject(prefix, item); level--; } } else { WriteObject(prefix, item); } } } else { MemberInfo[] members = element.GetType().GetMembers(BindingFlags.Public | BindingFlags.Instance); WriteIndent(); Write(prefix); bool propWritten = false; foreach (MemberInfo m in members) { FieldInfo f = m as FieldInfo; PropertyInfo p = m as PropertyInfo; if (f != null || p != null) { if (propWritten) { WriteTab(); } else { propWritten = true; } Write(m.Name); Write("="); Type t = f != null ? f.FieldType : p.PropertyType; if (t.IsValueType || t == typeof(string)) { WriteValue(f != null ? f.GetValue(element) : p.GetValue(element, null)); } else { if (typeof(IEnumerable).IsAssignableFrom(t)) { Write("..."); } else { Write("{ }"); } } } } if (propWritten) WriteLine(); if (level < depth) { foreach (MemberInfo m in members) { FieldInfo f = m as FieldInfo; PropertyInfo p = m as PropertyInfo; if (f != null || p != null) { Type t = f != null ? f.FieldType : p.PropertyType; if (!(t.IsValueType || t == typeof(string))) { object value = f != null ? f.GetValue(element) : p.GetValue(element, null); if (value != null) { level++; WriteObject(m.Name + ": ", value); level--; } } } } } } } } private void WriteValue(object o) { if (o == null) { Write("null"); } else if (o is DateTime) { Write(((DateTime)o).ToShortDateString()); } else if (o is ValueType || o is string) { Write(o.ToString()); } else if (o is IEnumerable) { Write("..."); } else { Write("{ }"); } } }
然后我们再Main方法中调用DisplayProcess方法:
static void Main(string[] args) { DisplayProcess(); Console.ReadKey(); }
输出结果:(部分)
好的,我主要来分析一下DisplayProcess方法中的代码:
//匿名类型 processes.Add(new { Id = process.Id, Name = process.ProcessName, Memory = process.WorkingSet64 }
你们有没有发现,new关键字后面并没有跟具体的类型名称,这就是我们的匿名类型语法,并不需要指定类型名称,编译器自动的帮我们生成这样的一个类,使用匿名类型的好处就是,如果某类型在代码中只出现一次,并没有多次使用,而且我们在程序中可以自由的组合某个类的结构,减少代码量,灵活性更好。
哎呀,累死了,说了这么长时间,今天的内容有点枯燥,我也不知道如何讲才能说的生动点,后面的文章我尽量说得生动些,大家就凑合看吧!如果你觉得这篇文章对你有所帮助,那么我的效果就达到了,另外,请不要吝啬你的支持,好文要顶。thank you...
技术讨论QQ群:159227188 上海-Michael
技术博客网站:http://www.kencery.com/