LINQ的基本功能就是创建操作管道,以及这些操作需要的任何状态。
为了富有效率的使用数据库和其他查询引擎,我们需要一种不同的方式表示管道中的各个操作。即把代码当作可在编程中进行检查的数据。
Lambda表达式不仅可以用他们创建委托实例,而且C#编译器也能将他们转换成表达式树——用于表示Lambda表达式逻辑的一种数据结构。简言之——Lambda表达式用符号语言习惯的方法来表示LINQ数据管线中的操作。
作为委托的Lambda表达式
Lambda有特殊转换规则:表达式的类型本身并非委托类型,但它可以通过多种方式隐式或显示的转换成一个委托实例。
匿名函数这个术语同时涵盖了匿名方法和Lambda表达式
转换成Lambda表达式
#region 9-1用匿名方法来创建委托实例
Func<string, int> returnLength;//等价public delegate int SomeDelegate(string arg1)。当返回void时,使用Action<>系列委托
returnLength = delegate(string text) { return text.Length; };
Console.WriteLine(returnLength("Holle"));
#endregion
#region 9-2冗长的第一个Lambda表达式
Func<string, int> returnLength;
returnLength = (string text) => { return text.Length; };
returnLength = (string text) => text.Length;//用单一表达式做为主体
returnLength = (text) => text.Length;//隐式类型的参数列表
returnLength = text => text.Length;//单一参数的快捷语法
Console.WriteLine(returnLength("Holle"));
#endregion
匿名方法中控制返回语句的规则同样不适用于Lambda表达式:不能从Lambda表达式返回void类型,如果有一个非void的返回类型,那么每个代码路径都必须返回一个兼容值。
Lambda表达式的主体可以包含另一个Lambda表达式,表达式参数可以是另一个委托。
使用List<T>和事件的简单例子

#region 9-4用Lambda处理一个电影列表
var films = new List<Film>
{
new Film{Name="Jaws1",Year=1975},
new Film{Name="Jaws2",Year=1975},
new Film{Name="Jaws5",Year=1975},
new Film{Name="Jaws4",Year=1976},
};
Action<Film> print = film => Console.WriteLine("Name={0},Year={1}", film.Name, film.Year);
 
films.ForEach(print);//打印全部元素
 
films.FindAll(film => film.Year > 1975).ForEach(print);//过滤打印
 
films.Sort((f1, f2) => f1.Name.CompareTo(f2.Name));//排序打印
films.ForEach(print);
 
#endregion
在事件处理程序中进行记录
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Button button = new Button { Text = "Click me" };
button.Click += (src, e) => Log("Click", src, e);
button.KeyPress += (src, e) => Log("KeyPress", src, e);
button.MouseClick+=(src,e)=>Log("MouseClick",src,e);
 
Form form = new Form { AutoSize = true, Controls = { button } };
Application.Run(form);
}
 
static void Log(string title, object sender, EventArgs e)
{
Console.WriteLine("Event:{0}", title);
Console.WriteLine("Sender:{0}", sender);
Console.WriteLine("Argument:{0}", e);
foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(e))
{
string name = prop.DisplayName;
object value = prop.GetValue(e);
Console.WriteLine(" {0}={1}", name, value);
}
}
static void Log(string title, object sender)
{
Console.WriteLine("Event:{0}", title);
Console.WriteLine("Sender:{0}", sender);
 
}
}
表达式树
表达式树是对象构成的树,树中每个节点本身都是一个表达式。不同的表达式类型代表能在代码中执行的不通操作:二元操作(加法)一元操作(获取一个数字长度),方法调用,构造函数等。
Expressions命名空间代表表达式各个类,它们都继承至Expressions
Expressions类两个属性:
  • Type属性代表表达式求值后的.NET类型
  • NodeType属性返回所代表的表达式种类
#region 9-6一个非常简单的表达式树
//System.Linq.Expressions命名空间包含了代表表达式的各个类,他们都继承至Expression
Expression firstArg = Expression.Constant(2);
Expression secondArg = Expression.Constant(3);
Expression add = Expression.Add(firstArg, secondArg);
Console.WriteLine(add);
#endregion
“叶”表达式在代码中最先创建:你自下而上构建了这些表达式。这是由于“表达式不易变”这一事实决定的——创建好表达式后,它就永远不会改变。
                                                                                       表达式图形化表示
将表达式树编译成委托
LambdaExpression是从Expression派生的类型之一。泛型类Expression<TDelegate>又是从LambdaExpression派生的。
                  从Expression<TDelegate>上溯至Expression的层次结构
Expression和Expression<TDelegate>类区别在于,泛型类以静态类型的方法标识了它是什么种类的表达式,也就是说,它确定了返回类型和参数。
#region 9-7编译并执行一个表达式
Expression firstArg = Expression.Constant(2);
Expression secondArg = Expression.Constant(3);
Expression add = Expression.Add(firstArg, secondArg);
 
Func<int> compiled = Expression.Lambda<Func<int>>(add).Compile();
Console.WriteLine(compiled());
#endregion
将C#Lambda表达式转换成表达式树
Lambda表达式能隐式或显示的转换成恰当的委托实例。还可以要求编译器通过你的Lambda表达式构建一个表达式树,在执行时创建Expression<TDelegate>的一个实例。
#region 9-8用Lambda表达式转换成表达式树
Expression<Func<int>> return5 = () => 5;//Lambda表达式
Func<int> compiled1 = return5.Compile();
Console.WriteLine(compiled1());
#endregion
并非所有得Lambda表达式都能转换成表达式树。不能将带有一个语句块的Lambda转换成表达式树——只有对单个表达式进行求值的Lambda才可以。表达式中海不能包含赋值操作,在表达式树中表达不了这种操作。
#region 9-9演示一个更复杂的表达式树
Expression<Func<string, string, bool>> expression = (x, y) => x.StartsWith(y);
var compiled = expression.Compile();
 
Console.WriteLine(compiled("First", "Second"));
Console.WriteLine(compiled("First", "Fir"));
#endregion
#region 9-10用代码来构造一个方法调用表达式树
MethodInfo method = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });//获取方法名为StartsWith,参数为string的公共方法
var target = Expression.Parameter(typeof(string), "x");
var methodArg = Expression.Parameter(typeof(string), "y");
Expression[] methodArgs = new[] { methodArg };
 
//Call(Expression instance, MethodInfo method, params Expression[] arguments)
Expression call = Expression.Call(target, method, methodArgs);//x.StartsWith(y),以上部件创建CallExpression
 
var lambdaParameters = new[] { target, methodArg };//这里使用的参数顺序就是调用委托所使用的参数顺序
var lambda = Expression.Lambda<Func<string, string, bool>>(call, lambdaParameters);//(x,y)=>x.StartsWith(y),lambdaParameters填充call集合
var compiled = lambda.Compile();//生成lambda表达式的委托
 
Console.WriteLine(compiled("First", "csend"));
Console.WriteLine(compiled("First", "Fir"));
#endregion
 
位于LINQ核心的表达式树
Lambda表达式提供了编译时检查的能力,而表达式可以将执行模型从你所需要的逻辑中提取出来。进程外的LINQ提供器的中心思想在于,我们可以从一个熟悉的源语言(C#)生成一个表达式树,将结果作为一个中间格式,在将其转换成目标平台的本地语言(SQL)
类型推断和重载决策的改变
精简泛型方法调用
用一个Lambda表达式调用一个泛型方法,同时传递一个隐式类型的参数列表,编译器就必须推断出你想要的是什么类型,然后才能检查出Lambda表达式主体
#region 9-11 需要新的类型推断规则例子(用Lambda表达式调用一个泛型方法)
static void PrintConvertedValue<TInput, TOutput>(TInput input, Converter<TInput, TOutput> converter)
{
Console.WriteLine(converter(input));
}
#endregion
#region 9-11
PrintConvertedValue("I'm a string", x => x.Length);//C#2中,编译将失败,C#2类型推断单独针对每一个实参来进行的,从一个实参无法推断出另一个实参
#endregion
推断匿名函数的返回类型
#region 9-13根据一天当中的时间来选择返回int或object
delegate T MyFunc<T>();
 
static void WriteResult<T>(MyFunc<T> function)
{
Console.WriteLine(function());
}
#endregion
#region 9-13
WriteResult(delegate
{
if (DateTime.Now.Hour < 22)//int
{
return 10;
}
else//object
{
return new object();
}
});//编译器采用处理隐式数组的逻辑处理返回类型,对int进行了装箱,返回类型为object
#endregion
分两阶段进行类型推断
#region 9-14综合来自多个实参的信息,灵活地进行类型推断
static void PrintType<T>(T first, T second)//被强制转换为具体类型参数的最终固定变量类型
{
Console.WriteLine(typeof(T));
}
#endregion
#region 9-14
PrintType(1,new object());//返回类型推断为object类型
#endregion
类型推断分两阶段进行:第一阶段处理的是普通的实参,其类型是一开始便知道的,这包括那些参数列表是显示类型的匿名函数
第二阶段是推断隐式类型的Lambda表达式和方法组的类型,其思想是,根据以拼凑的信息,判断是否足够推断出Lambda表达式的参数类型。
#region 9-15多级类型推断
static void ConvertTwice<TInput, TMiddle, TOutput>(TInput input, Converter<TInput, TMiddle> firstConversion, Converter<TMiddle, TOutput> secondConversion)
{
TMiddle middle = firstConversion(input);
TOutput output = secondConversion(middle);
Console.WriteLine(output);
}
#endregion
#region 9-15
//第一阶段,编译器处理普通实参,得到TInput类型string,第一次执行阶段二,TInput固定为string类型,推断TMiddle为int,再次执行第二阶段,TMiddle固定为int,TOutput为double,推断结束
//lambda表达式主体只有在输入参数的类型已知后才能进行检查
ConvertTwice("Another string", text => text.Length, length => Math.Sqrt(length));
#endregion
选择正确的被重载方法
#region 9-16委托返回类型影响了重载选择
//如果一个匿名函数能转换成参数列表相同,但返回类型不同的两个委托类型,就根据从“推断的返回类型”到“委托的返回类型”的转换来判断哪个委托转换好
static void Execute(Func<int> action)
{
Console.WriteLine("action return an int:" + action());
}
static void Execute(Func<double> action)
{
Console.WriteLine("action return a douban:" + action());
}
#endregion
#region 9-16
Execute(() => 1);
#endregion

 

Lambda表达式和Lambda表达式树的更多相关文章

  1. 委托、匿名委托、Lambda 表达式、Expression表达式树之刨根问底

    本篇不是对标题所述之概念的入门文章,重点在阐述它们的异同点和应用场景.各位看官,这里就不啰嗦了,直接上代码. 首先定义一个泛型委托类型,如下: public delegate T Function&l ...

  2. C#中分别对委托、匿名方法、Lambda表达式、Lambda表达式树以及反射执行同一方法的过程进行比较。

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  3. (转) Lambda表达式中的表达式lambda和语句lambda区别

    Lambda表达式可分为表达式lambda和语句lambda 表达式lambda:表达式位于 => 运算符右侧的lambda表达式称为表达式lambda (input parameters) = ...

  4. Lambda表达式中的表达式lambda和语句lambda区别

    Lambda表达式可分为表达式lambda和语句lambda 表达式lambda:表达式位于 => 运算符右侧的lambda表达式称为表达式lambda (input parameters) = ...

  5. C#3.0之神奇的Lambda表达式和Lambda语句

    “Lambda 表达式”是一个匿名函数,它可以包含表达式和语句,并且可用于创建委托或表达式目录树类型.所有 Lambda 表达式都使用 Lambda 运算符 =>,该运算符读为“goes to” ...

  6. Linq表达式、Lambda表达式你更喜欢哪个?

    什么是Linq表达式?什么是Lambda表达式? 如图: 由此可见Linq表达式和Lambda表达式并没有什么可比性. 那与Lambda表达式相关的整条语句称作什么呢?在微软并没有给出官方的命名,在& ...

  7. Linq表达式和Lambda表达式用法对比

    什么是Linq表达式?什么是Lambda表达式?前一段时间用到这个只是,在网上也没找到比较简单明了的方法,今天就整理了一下相关知识,有空了再仔细研究研究 public Program() { List ...

  8. lambda表达式和查询表达式

    (1)Lambda表达式定义: Lambda是创建匿名函数的另一种形式.它比对应的匿名方法更加的简化.因此,所有的情况都推荐使用Lambda表达式.   它可以包括表达式和语句,并且用于创建委托和事件 ...

  9. 匿名函数 lambda表达式(lambda expression)

    阅读g2log时,发现有两行代码居然看不懂. 1. auto bg_call =  [this, log_directory]() {return pimpl_->backgroundChang ...

随机推荐

  1. Caused by: java.lang.RuntimeException: java.io.IOException: invalid constant type: 18

    工程启动的时候有报下面这个错误的,更新下工程的jar包依赖,然后在工程的pom文件里加上下面的jar包 Caused by: java.lang.RuntimeException: java.io.I ...

  2. 计算两个YUV420P像素数据的PSNR---高等算法

    PSNR是最基本的视频质量评价方法.本程序中的函数可以对比两张YUV图片中亮度分量Y的PSNR.函数的代码如下所示. /** * Calculate PSNR between 2 YUV420P fi ...

  3. 搭建ftp服务器实现文件共享

    FTP服务器(File Transfer Protocol Server)是在互联网上提供文件存储和访问服务的计算机,它们依照FTP协议提供服务. FTP(File Transfer Protocol ...

  4. angular element()

    使用angular.element()获取一个dom的方法. 1.可以使用jquery的选择器 2.可以使用javascript的原生的的查找元素的方法 下面是angular.element()提供的 ...

  5. 将基因组数据分类并写出文件,python,awk,R data.table速度PK

    由于基因组数据过大,想进一步用R语言处理担心系统内存不够,因此想着将文件按染色体拆分,发现python,awk,R 语言都能够非常简单快捷的实现,那么速度是否有差距呢,因此在跑几个50G的大文件之前, ...

  6. linux 下日常使用便利工具

    Nautilus 你工作中有在GUI和命令行之间切来切去吗?当你总是要在命令行中输入你要进入的目录的时候,你有沮丧无奈过吗?如果有的话,那么,你一定要试下这个nautilus插件 —— nautilu ...

  7. Selenium测试专项三班隆重开班

    Selenium测试专项三班隆重开班 应广大测试技术人员要求,以及企业技术需求.Selenium提前一周开课了,只针对合作的每家企业提供1-2个参训名额.预计培训60人次.但报名人数却远远超出我们预期 ...

  8. 老李分享:为何要使用 Web Services

    老李分享:为何要使用 Web Services   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家咨询q ...

  9. [编织消息框架][JAVA核心技术]动态代理应用5-javassist

    基础部份: 修改class我们用到javassist,在pom.xml添加 <properties> <javassist.version>3.18.2-GA</java ...

  10. python作业设计:输入用户名密码,认证成功后显示欢迎信息,输错三次后锁定

    作业需求: 1.输入用户名密码 2.认证成功后显示欢迎信息 3.输错三次后锁定实现思路: 1.判断用户是否在黑名单,如果在黑名单提示账号锁定. 2.判断用户是否存在,如果不存在提示账号不存在. 3.判 ...