zoukankan      html  css  js  c++  java
  • LinQ实战学习笔记(二) C#增强特性

    C# 为支持LINQ添加了许多语言特性:

    • 隐式类型局部变量
    • 对象初始化器
    • Lambda表达式
    • 扩展方法
    • 匿名类型

    了解这些新特性是全面了解LINQ的重要先解条件,因此请不要忽视它们.

    (一)  隐式类型局部变量

    processData这个类中的亮点是 {get;set;} 它是一个新特性, 系统会自动产生一个匿名私有变量.

     1 public Int32 Id { get; set; }
     2         public Int64 Memory { get; set; }
     3         public string Name { get; set; }
     4 
     5         public processData()
     6         {
     7 
     8         }
     9 
    10         public processData(Int32 id, Int64 mem, string name)
    11         {
    12             Id = id;
    13             Memory = mem;
    14             Name = name;
    15         }

    var让你无需要写两次变量的类型, 编译器会自动推导,可以有效的减少代码书写量.

    ObjectDumper.write() 是一个自定义的类, 在本系列的源代码中会有提供, 它可以方便显示对象的内容.

     1   var process = new List<processData>();
     2             foreach (var m in Process.GetProcesses())
     3             {
     4                 var data = new processData();
     5                 data.Id = m.Id;
     6                 data.Name = m.ProcessName;
     7                 data.Memory = m.WorkingSet64;
     8                 process.Add(data);
     9             }
    10             ObjectDumper.Write(process);

    运行结果:

    (二)  对象初始化器

    上面的代码可以利用processData类的构造函数进行优化.

    1 //用构造函数优化上面的代码
    2             var process = new List<processData>();
    3             foreach (var m in Process.GetProcesses())
    4             {
    5                 process.Add(new processData(m.Id, m.WorkingSet64, m.ProcessName));
    6             }
    7             ObjectDumper.Write(process);

    最佳做法是使用对象初始化器优化, 如下:

     1  //用对象初始化器优化上面的代码
     2             var process = new List<processData>();
     3             foreach (var m in Process.GetProcesses())
     4             {
     5                 process.Add(new processData()
     6                 { 
     7                     Id = m.Id,
     8                     Memory = m.WorkingSet64,
     9                     Name = m.ProcessName
    10                 });
    11             }
    12             ObjectDumper.Write(process);

    在你敲上面代码时, 在{}中敲一下空格,  对象初始化器还可以支持智能感知. 相当方便.

    从上面代码可以看出,有了对象初始化器, 好处很多:

    • 只需要一条语句完成对象初始化工作
    • 无需为简单对象提供构造函数
    • 无需要为实始化不同的属性而为类提供多个构造函数

    (三)  Lambda表达式

    要解释Lambda表达式首先要解释下面几个知识:

    • 委托
    • 匿名方法

    1. 委托

     C#的委托用处很大, 比如更新UI界面, 或者发挥类似函数指针的作用.

    下面的例子中委托的作用类似于函数指针.

     1         delegate bool match<T>(T obj);
     2         private void disProcess(match<Process> fun)
     3         {
     4             var process = new List<processData>();
     5             foreach (var m in Process.GetProcesses())
     6             {
     7                 if (fun(m))
     8                 {
     9                     process.Add(new processData()
    10                     {
    11                         Id = m.Id,
    12                         Memory = m.WorkingSet64,
    13                         Name = m.ProcessName
    14                     });
    15                 }
    16             }
    17             ObjectDumper.Write(process);
    18         }
    19         bool filter(Process obj)
    20         {
    21             return obj.WorkingSet64 > 10 * 1024 * 1024;
    22         }
    23         private void button7_Click(object sender, EventArgs e)
    24         {
    25             disProcess(filter);
    26         }

     结果是筛选出大于10M的进程信息.

    2. 匿名方法

    匿名方法不用显式定义方法名, 降低了代码量.

    下面的代码, 相比之前的代码来说, 就是省略了filter函数的名字, 只用了它的函数体代码.

    代码改进部分如下:

    1   //演示匿名方法
    2             disProcess(delegate(Process pro)
    3             {
    4                 return pro.WorkingSet64 > 10 * 1024 * 1024;
    5             });

    C#在List<T>和Array类型中添加了一些方法, 如ForEach,Find以方便使用匿名方法

    下面我们修改下disProcess() 函数, 演示一下对process使用Find方法查找进程devenv.

     1  private void disProcess(match<Process> fun)
     2         {
     3             var process = new List<processData>();
     4             foreach (var m in Process.GetProcesses())
     5             {
     6                 
     7                 if (fun(m))
     8                 {
     9                     process.Add(new processData()
    10                     {
    11                         Id = m.Id,
    12                         Memory = m.WorkingSet64,
    13                         Name = m.ProcessName
    14                     });
    15                 }
    16             }
    17 
    18             //C#在List<T>和Array类型中添加了一些方法, 如ForEach,Find以方便使用匿名方法
    19             var list1= process.Find(delegate(processData obj)
    20             {
    21                 return obj.Name == "devenv";
    22             });
    23 
    24             ObjectDumper.Write(process);
    25         }

    下面我们可以直接使用Lambda表达式, 代替匿名方法.

    1 //演示Lambda表达式
    2 //读作, 对传入的Process对象,如果内存占用超过10M,则返回true
    3  disProcess(s => s.WorkingSet64 > 10 * 1024 * 1024);

    Lambda表达式的好处是, 无需要提供参数类型, 由编译器从方法的签名中自动取得.

    除了实现匿名方法的功能外, Lambda还提供额外的好处:

    • 无需要提供参数类型
    • 支持用语句块或者表达式作为方法体, 匿名方法只能使用语句块
    • 支持通过参数选择重载函数
    • 带有表达式体的Lambda表达式能够转化为表达式树

     (四) 扩展方法

    下面的例子要做一件事:查找内在占用大于10M的进程,计算总内存占用,转为MB方式表示

    相对于使用普通方法,如果使用扩展方法来实现,它的语法结构易于使程序员把许多操作用.串联起来.

    这样代码的实际执行顺序和阅读顺序就完全致.完全符合人的思维.

    另一方面,扩展方法被智能感知支持,你要类型后.一下,就可以快速找到扩展方法.

     1   private void button10_Click(object sender, EventArgs e)
     2         {
     3             //我们编写三个扩展方法,按顺序执行得到结果
     4             //1. FilterOutSomeProcess()   过滤出一部分符合条件的Process
     5             //2. TotalMemory()            统计这些Process的内存总占用
     6             //3. BytesToMegaBytes()       把上述内存占用转为MB方式表示
     7 
     8             //查找内在占用大于10M的进程,计算总内存占用,转为MB方式表示.
     9             Console.WriteLine("查找内在占用大于10M的进程,计算总内存占用,转为MB方式表示:" 
    10                 + Environment.NewLine);
    11             Console.WriteLine(
    12             Process.GetProcesses()
    13                 .FilterOutSomeProcess(filter2)
    14                 .TotalMemory()
    15                 .BytesToMegaBytes()+"MB"
    16             );
    17         }
    18 
    19         bool filter2(long mem)
    20         {
    21             if (mem > 10 * 1024 * 1024) return true;
    22             return false;
    23         }

     下面是上面三个扩展方法的定义部分.

    可以看见,扩展方法在要扩展类的类型前面加上关键字this, 其它的与普通方法没什么不同.

    在智能感知时,扩展方法图标有个向下的小箭头,以和普通方法区别开来.

     1  delegate bool filterDelegate(long mem);
     2     static class externFun
     3     {
     4         public static List<processData> FilterOutSomeProcess(this Process[] pro,filterDelegate fun)
     5         {
     6             var proList = new List<processData>();
     7             foreach (var m in pro)
     8             {
     9                 if(fun(m.WorkingSet64))
    10                     proList.Add(new processData()
    11                     {
    12                         Id = m.Id,
    13                         Memory = m.WorkingSet64,
    14                         Name = m.ProcessName
    15                     });
    16             }
    17             return proList;
    18         }
    19 
    20         public static long TotalMemory(this List<processData> data)
    21         {
    22             long sum=0;
    23             data.ForEach(s => sum += s.Memory);
    24             return sum;
    25         }
    26 
    27         public static float BytesToMegaBytes(this long sum)
    28         {
    29             return (float)sum / 1024f / 1024f;
    30         }
    31 
    32     }

    结果如下:

    LINQ也带来一系列扩展方法,它们也可以不用于LINQ中.使用它们要包含 using System.Linq

    下面我们介绍几个:

    OrderByDescending

    Take

    Sum

    我们通过一个例子介绍上面三个扩展方法.

    这个例子是想实现: 进程按内存占用排序,取前两名耗内存大户并计算它们耗费的总内存,用MB表示

     1  private void button11_Click(object sender, EventArgs e)
     2         {
     3             var list1 = new List<processData>();
     4             foreach (var m in Process.GetProcesses())
     5             {
     6                 list1.Add(new processData()
     7                 {
     8                     Id = m.Id,
     9                     Memory = m.WorkingSet64,
    10                     Name = m.ProcessName
    11                 });
    12             }
    13             Console.WriteLine("进程按内存占用排序,取前两名耗内存大户并计算它们耗费的总内存,用MB表示"+
    14                 Environment.NewLine);
    15             Console.WriteLine(
    16             list1
    17                 .OrderByDescending(s => s.Memory)
    18                 .Take(2)
    19                 .Sum(s => s.Memory) / 1024f / 1024f +"MB"
    20             );
    21         }

     关于扩展方法的几点说明:

    1. 扩展方法必须定义于静态类,自身应该为public,static

    2. 如果实例方法和扩展方法同名,先执行实例方法,扩展方法执行优先级要低.

    3. 扩展方法无法访问类型的非公有成员.

    (五) 匿名类型

    有了匿名类型,我们可以不再使用上面定义的processData类.而完成了同样的事情.

     1  private void button12_Click(object sender, EventArgs e)
     2         {
     3             var list1 = new List<object>();
     4             foreach (var m in Process.GetProcesses())
     5             {
     6                 //匿名类型可以不指定属性的名称, 指定属性的名称仅仅是为了让代码好懂些
     7                 list1.Add(
     8                 new 
     9                 {
    10                     //Id = m.Id,
    11                     //Memory = m.WorkingSet64,
    12                     //Name = m.ProcessName
    13                     m.Id,
    14                     m.WorkingSet64,
    15                     m.ProcessName
    16                 });
    17             }
    18             ObjectDumper.Write(list1);
    19 
    20 
    21             var obj = ReturnAGeneric(() => new
    22             {
    23                 Time = DateTime.Now,
    24                 Name = "hackpig"
    25             });
    26             //下面的代码是非法的, 这是因为匿名类型的实例是不可变的,它没有set
    27             //obj.Name = "lxy1";
    28             //obj.Time = DateTime.Now;
    29             ObjectDumper.Write(obj);
    30 
    31         }
    32 
    33         public static TResult ReturnAGeneric<TResult>(Func<TResult> creator)
    34         {
    35             return creator();
    36         }

    匿名类型的好处是无须特地编写专门的类型来存放这些临时性数据,可以大大加快程序的编写速度.

    匿名类型有一些限制,如下:

    • 在定义匿名类型的方法外想操作匿名类型,只有依靠反射.
    • 匿名类型只适合存放临时的数据,不能做为方法返回类型.(泛型返回值的方法中除外)
    • 匿名类型一旦创建了实例,那么这个实例的值就固定不可变的,也就是不允许再set了.之所以这样限制,就是要为函数式编程基于值的编程风格服务,避免不必要的副作用.另外在PLINQ中,这个特性至关重要,永远不会改变的对象大大降低了程序设计中对并发处理的难度.

    这本书前两章的内容到此就结束了,笔者感觉讲解得还是蛮精彩的.

    下面放上练习的源代码,这些代码,有些已经不同于源书代码,是在理解的基础上改编的.

    本文源代码

    原创文章,出自"博客园, 猪悟能'S博客" : http://www.cnblogs.com/hackpig/

  • 相关阅读:
    delphi7在windows server 2003企业版上不能打开项目的选项(Options)窗口的解决方法
    简单的两个字“谢谢”,会让我坚持我的写作,我也要谢谢你们
    F41GUT 安装Windows server 2003系统后无法安装显卡驱动的解决办法
    远程桌面无法登录windows server 2003服务器
    F41GUT 安装Windows server 2003系统后无法安装显卡驱动的解决办法
    MS SQL Server 2000版在windows server 2003企业版系统上运行时造成数据库suspect的解决方法
    delphi7在windows server 2003企业版上不能打开项目的选项(Options)窗口的解决方法
    远程桌面无法登录windows server 2003服务器
    MS SQL Server 2000版在windows server 2003企业版系统上运行时造成数据库suspect的解决方法
    关于ajax 和josn
  • 原文地址:https://www.cnblogs.com/hackpig/p/5810907.html
Copyright © 2011-2022 走看看