• Linq学习之路(03) 什么使Linq变得如此简单(二)?


      我们接着上篇Linq系列文章继续谈Linq的基石,上篇文章中,我们谈到了隐式类型局部变量以及对象集合初始化器,今天我们说说Lambda表达式、扩展方法和匿名类型。按计划,这篇文章昨天就应该写出来了,可是昨天确实很累,给自己定的目标是每天都写一篇技术文章,来总结每天学习的成果。又给自己的懒惰找借口了。。。

      好吧,言归正传。对了,昨天马云好像辞去了阿里巴巴CEO一职,哎,羡慕啊,向他学习。希望能从他身上学点东西……

      呵呵,又扯了,这次真的进入到主题。我们接着上篇文章中用到的实例,我们继续给这个实例进行进一步的优化。现在,我想给DisplayProcess方法添加一个过滤条件,输出占用内存空间大于20M的进程,首先我利用“硬编码”来实现过滤,其他代码不变主要是在DisplayProcess方法中添加一个if判断,好的,看代码:

    DisplayProcess
            /// <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方法:把过滤器作为参数传进去

    DisplayProcess
            /// <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

    Filter
            /// <summary>
            /// 过滤器
            /// </summary>
            /// <param name="process">参数</param>
            /// <returns>返回值,true或false</returns>
            static Boolean Filter(Process process)
            {
                return process.WorkingSet64 >= 20 * 1024 * 1024;
            }

      在Mian函数中调用DisplayProcess方法时将Filter传进去:

    Main
            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,好,我们进入今天的第二个重点:扩展方法。

    重点二:扩展方法

      在开始扩展方法之前,我们引进一个小插曲,也就是在上面的实例中,我们想假如一个统计所有进程占用的内存总大小的方法。首先我们想到的就是添加一个统计的方法不就得了,好的,我根据大家的思维来写这样的一个方法:

    TotalMemory
            /// <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方法:

    DisplayProcess
            /// <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函数中列出所有进程,不添加过滤条件:

    Main
            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的方法:

    TotalMemory
            /// <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修饰:我把整个静态类代码贴出来。

    Static Class Program
       //必须为静态类
        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这个实体类,同样我们完成输出所有的进程:

    DisplayProcess
            //展示进程的方法
            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类,他的功能主要是在控制台显示数据:代码我贴在下面

    ObjectDumper
    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/

  • 相关阅读:
    关于华为x1 7.0无法从eclipse发布的更新as发布的apk
    2015-10-29
    Android Exception18(Stuido debug .....)
    阿里巴巴校招运营专员笔试题
    业务性产品经理(商业领域)笔试题
    Android编程之常识
    2015-08-24
    What most young programmers need to learn
    hdu 1556 Color the ball
    跟着实例学习设计模式(3)-工厂方法(创建型)
  • 原文地址:https://www.cnblogs.com/ARMdong/p/3074183.html
Copyright © 2020-2023  润新知