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

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

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

(一)  隐式类型局部变量

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

 public Int32 Id { get; set; }
public Int64 Memory { get; set; }
public string Name { get; set; } public processData()
{ } public processData(Int32 id, Int64 mem, string name)
{
Id = id;
Memory = mem;
Name = name;
}

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

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

   var process = new List<processData>();
foreach (var m in Process.GetProcesses())
{
var data = new processData();
data.Id = m.Id;
data.Name = m.ProcessName;
data.Memory = m.WorkingSet64;
process.Add(data);
}
ObjectDumper.Write(process);

运行结果:

(二)  对象初始化器

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

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

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

  //用对象初始化器优化上面的代码
var process = new List<processData>();
foreach (var m in Process.GetProcesses())
{
process.Add(new processData()
{
Id = m.Id,
Memory = m.WorkingSet64,
Name = m.ProcessName
});
}
ObjectDumper.Write(process);

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

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

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

(三)  Lambda表达式

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

  • 委托
  • 匿名方法

1. 委托

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

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

         delegate bool match<T>(T obj);
private void disProcess(match<Process> fun)
{
var process = new List<processData>();
foreach (var m in Process.GetProcesses())
{
if (fun(m))
{
process.Add(new processData()
{
Id = m.Id,
Memory = m.WorkingSet64,
Name = m.ProcessName
});
}
}
ObjectDumper.Write(process);
}
bool filter(Process obj)
{
return obj.WorkingSet64 > * * ;
}
private void button7_Click(object sender, EventArgs e)
{
disProcess(filter);
}

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

2. 匿名方法

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

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

代码改进部分如下:

   //演示匿名方法
disProcess(delegate(Process pro)
{
return pro.WorkingSet64 > * * ;
}
);

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

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

  private void disProcess(match<Process> fun)
{
var process = new List<processData>();
foreach (var m in Process.GetProcesses())
{ if (fun(m))
{
process.Add(new processData()
{
Id = m.Id,
Memory = m.WorkingSet64,
Name = m.ProcessName
});
}
} //C#在List<T>和Array类型中添加了一些方法, 如ForEach,Find以方便使用匿名方法
var list1= process.Find(delegate(processData obj)
{
return obj.Name == "devenv";
});

ObjectDumper.Write(process);
}

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

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

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

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

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

 (四) 扩展方法

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

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

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

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

   private void button10_Click(object sender, EventArgs e)
{
//我们编写三个扩展方法,按顺序执行得到结果
//1. FilterOutSomeProcess() 过滤出一部分符合条件的Process
//2. TotalMemory() 统计这些Process的内存总占用
//3. BytesToMegaBytes() 把上述内存占用转为MB方式表示 //查找内在占用大于10M的进程,计算总内存占用,转为MB方式表示.
Console.WriteLine("查找内在占用大于10M的进程,计算总内存占用,转为MB方式表示:"
+ Environment.NewLine);
Console.WriteLine(
Process.GetProcesses()
.FilterOutSomeProcess(filter2)
.TotalMemory()
.BytesToMegaBytes()+"MB"

);
} bool filter2(long mem)
{
if (mem > * * ) return true;
return false;
}

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

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

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

  delegate bool filterDelegate(long mem);
static class externFun
{
public static List<processData> FilterOutSomeProcess(this Process[] pro,filterDelegate fun)
{
var proList = new List<processData>();
foreach (var m in pro)
{
if(fun(m.WorkingSet64))
proList.Add(new processData()
{
Id = m.Id,
Memory = m.WorkingSet64,
Name = m.ProcessName
});
}
return proList;
} public static long TotalMemory(this List<processData> data)
{
long sum=;
data.ForEach(s => sum += s.Memory);
return sum;
} public static float BytesToMegaBytes(this long sum)
{
return (float)sum / 1024f / 1024f;
} }

结果如下:

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

下面我们介绍几个:

OrderByDescending

Take

Sum

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

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

  private void button11_Click(object sender, EventArgs e)
{
var list1 = new List<processData>();
foreach (var m in Process.GetProcesses())
{
list1.Add(new processData()
{
Id = m.Id,
Memory = m.WorkingSet64,
Name = m.ProcessName
});
}
Console.WriteLine("进程按内存占用排序,取前两名耗内存大户并计算它们耗费的总内存,用MB表示"+
Environment.NewLine);
Console.WriteLine(
list1
.OrderByDescending(s => s.Memory)
.Take()
.Sum(s => s.Memory) / 1024f / 1024f +"MB"

);
}

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

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

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

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

(五) 匿名类型

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

  private void button12_Click(object sender, EventArgs e)
{
var list1 = new List<object>();
foreach (var m in Process.GetProcesses())
{
//匿名类型可以不指定属性的名称, 指定属性的名称仅仅是为了让代码好懂些
list1.Add(
new
{
//Id = m.Id,
//Memory = m.WorkingSet64,
//Name = m.ProcessName
m.Id,
m.WorkingSet64,
m.ProcessName
});
}
ObjectDumper.Write(list1); var obj = ReturnAGeneric(() => new
{
Time = DateTime.Now,
Name = "hackpig"
});
//下面的代码是非法的, 这是因为匿名类型的实例是不可变的,它没有set
//obj.Name = "lxy1";
//obj.Time = DateTime.Now;
ObjectDumper.Write(obj);

} public static TResult ReturnAGeneric<TResult>(Func<TResult> creator)
{
return creator();
}

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

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

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

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

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

本文源代码

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

LinQ实战学习笔记(二) C#增强特性的更多相关文章

  1. LinQ实战学习笔记(三) 序列,查询操作符,查询表达式,表达式树

    序列 延迟查询执行 查询操作符 查询表达式 表达式树 (一) 序列 先上一段代码, 这段代码使用扩展方法实现下面的要求: 取进程列表,进行过滤(取大于10M的进程) 列表进行排序(按内存占用) 只保留 ...

  2. LinQ实战学习笔记(一) LINQ to (Objects, XML, SQL) 入门初步

    LINQ对于笔者来说, 优美而浓缩的代码让人震惊. 研究LINQ就是在艺术化自己的代码. 之前只是走马观花学会了基本的语法, 但是经常在CSDN看到令人惊讶自叹不如的LINQ代码, 还是让人羡慕嫉妒恨 ...

  3. LinQ实战学习笔记(四) LINQ to Object, 常用查询操作符

    这一篇介绍了下面的内容: 查询object数组 查询强类型数组 查询泛型字典 查询字符串 SelectMany 索引 Distinct操作符 排序 嵌套查询 分组 组连接 内连接 左外连接 交叉连接 ...

  4. 基于.net的分布式系统限流组件 C# DataGridView绑定List对象时,利用BindingList来实现增删查改 .net中ThreadPool与Task的认识总结 C# 排序技术研究与对比 基于.net的通用内存缓存模型组件 Scala学习笔记:重要语法特性

    基于.net的分布式系统限流组件   在互联网应用中,流量洪峰是常有的事情.在应对流量洪峰时,通用的处理模式一般有排队.限流,这样可以非常直接有效的保护系统,防止系统被打爆.另外,通过限流技术手段,可 ...

  5. AJax 学习笔记二(onreadystatechange的作用)

    AJax 学习笔记二(onreadystatechange的作用) 当发送一个请求后,客户端无法确定什么时候会完成这个请求,所以需要用事件机制来捕获请求的状态XMLHttpRequest对象提供了on ...

  6. Django学习笔记二

    Django学习笔记二 模型类,字段,选项,查询,关联,聚合函数,管理器, 一 字段属性和选项 1.1 模型类属性命名限制 1)不能是python的保留关键字. 2)不允许使用连续的下划线,这是由dj ...

  7. ES6学习笔记<二>arrow functions 箭头函数、template string、destructuring

    接着上一篇的说. arrow functions 箭头函数 => 更便捷的函数声明 document.getElementById("click_1").onclick = ...

  8. amazeui学习笔记二(进阶开发1)--项目结构structure

    amazeui学习笔记二(进阶开发1)--项目结构structure 一.总结 1.项目结构:是说的amazeui在github上面的项目结构,二次开发amazeui用 二.项目结构structure ...

  9. WPF的Binding学习笔记(二)

    原文: http://www.cnblogs.com/pasoraku/archive/2012/10/25/2738428.htmlWPF的Binding学习笔记(二) 上次学了点点Binding的 ...

随机推荐

  1. Lemon OA第2篇:功能解析方法

    Lemon OA,整个系统功能也算是比较丰富,OA的很多功能都能看见影子,虽然做得不是很强大 接触Lemon OA,起源于Activiti的学习热情,既然这样,研究Lemon OA的目标有3: 1.L ...

  2. form中的button按钮在IE11中自动提交表单问题导致弹出框关闭之后表单被重置

    最近几天,测试系统,遇到一个兼容性问题,form中有一个button按钮,没有指定type类型,点击按钮弹出框选择值之后回填给form上的一个单行文本框,在IE6.IE7.IE8.IE9.IE10中测 ...

  3. Java VM

    何时需要理解Java 虚拟机机制 一.排错 二.性能优化 字节码文件的执行流程.机制. 涉及到文件的加载机制(类加载器).执行机制(执行引擎).运行时优化(JIT运行时编译).以及内存分配与垃圾回收. ...

  4. js 事件详解 冒泡

    起因:正常情况下我点击s2时是先弹出我是children,再弹出我是father,但是却出现了先弹出我是father,后弹出我是children的情况,这种情况是在和安卓app交互的h5页面中出现的, ...

  5. hql date比较

    补充:相等时用to_char,比较大小(<或>)时用 时间格式(如果不是时间格式可以用to_date) java.util.Date date=new java.util.Date(); ...

  6. [译]Angular-ui 之 Url Routing

    ◄ 前一篇 (Multiple Named Views)     下一篇 (The Components) ► 在你的应用中多数的状态都是基于特定的url地址的.Url Routing机制绝不是在状态 ...

  7. 【Deep Learning】RNN LSTM 推导

    http://blog.csdn.net/Dark_Scope/article/details/47056361 http://blog.csdn.net/hongmaodaxia/article/d ...

  8. NSIS 资料

    官方 http://nsis.sourceforge.net/Main_Page NSIS官方插件全集 http://az.eliang.com/aq_2013041703.html NSIS 衿华客 ...

  9. 如何在LINUX中安装VM-Tools

    1.运行VM,启动你的虚拟LINUX系统. 2.切换到原系统,选择VM中的VM菜单----Install VMWare Tools. 3.在虚拟机设置里,请把你的VM-Tools的ISO文件映射到你的 ...

  10. mysql中查看视图的元数据?

    需求描述: 查看视图的元数据的方法. 操作过程: 1.通过查看information_schema数据库下的views表来查看视图的定义语句 mysql> select definer,view ...