深⼊学习C#匿名函数、委托、Lambda表达式、表达式树类型
——Expressiontre。。。
匿名函数
  匿名函数(Anonymous Function)是表⽰“内联”⽅法定义的表达式。匿名函数本⾝及其内部没有值或者类型,但是可以转换为兼容的委托或者表达式树类型()。匿名函数转换的计算取决于转换的⽬标类型:如果是委托类型,则转换计算为引⽤匿名函数所定义的⽅法的委托;如果是表达式树类型,则转换将计算以对象结构形式表⽰⽅法结构的表达式树。
  匿名函数有两种语法风格:Lambda表达式(lambda-expression)和匿名⽅法表达式(anonymous-method-expression)。在⼏乎所有的情况下,Lambda表达式都⽐匿名⽅法表达式更为简介具有表现⼒。但现在C#语⾔中仍保留了后者,为了向后兼容。
  Lambda表达式:
    async可选 (匿名的函数签名)=> (匿名的函数体)
  匿名⽅法表达式:
    async可选 delegate (显式的匿名函数签名) 可选{代码块}
  其中匿名的函数签名可以包括两种,⼀种是隐式的匿名函数签名另⼀种是显式的匿名函数签名:
    隐式的函数签名:(p)、(p1,p1)
    显式的函数签名:(int p)、(int p1,int p2)、(ref int p1,out int p2)
  匿名的函数体可以是表达式或者代码块。
  从上⾯我们可以看出,Lambda表达式的参数形式可以显式或者隐式类型化。在显式类型化参数列表中,每个参数的类型是显式声明的,在隐式类型化参数列表中,参数的类型是从匿名函数出现的上下⽂中推断出来的。
  当Lambda表达式只有⼀个具有隐式类型化参数的时候,参数列表可以省略圆括号,也就是说:
  (参数) => 表达式
  可以简写为
  参数 => 表达式
⼀些匿名函数的⽰例
x => x + 1//隐式的类型化,函数体为表达式
x => {return x + 1;} //隐式的类型化,函数体为代码块
(int x) => x + 1//显式的类型化,函数体为表达式
(int x) => {return x + 1;} //显式的类型化,函数体为代码块
(x , y) => x * y //多参数
() => Console.WriteLine() //⽆参数
async (t1 , t2) => await t1 + await t2 //异步
delegate (int x) {return x + 1;} //匿名函数⽅法表达式
delegate {return1 + 1;} //参数列表省略 
Lambda表达式和匿名⽅法表达式的区别:
  ● 当没有参数的时候,匿名⽅法表达式允许完全省略参数列表,从⽽可以转换为具有任意值参数列表的委托类型,Lambda表达式则不能省略参数列表的圆括号()。
  ● Lambda表达式允许省略和推断类型参数,⽽匿名⽅法表达式要求显式声明参数类型。
  ● Lambda表达式主体可以为表达式或者代码块,⽽匿名⽅法表达式的主体必须为代码块。
  ● 只有Lambda表达式可以兼容到表达式树类型。
委托
  ⼀个委托是⼀个指向⼀个⽅法的引⽤,或者说,⼀个委托的实例就是⼀个指向某个⽅法的对象,这是⼀个简单却⼗分强⼤的概念。
  C#中的委托是⽤来处理在其他语⾔中(如C++、Pascal等)需要⽤函数指针来处理的情况。不过与C++不同的是:委托是完全⾯向对象的;C++指针仅仅指向成员函数,⽽委托同时封装了对象的实例和⽅法;委托是完全类型安全的,只有当函数的签名与委托的签名匹配的时候,委托才可以指向该⽅法,当委托没有合法的指向⽅法的时候不能被调⽤。
  ⼀些关于委托的知识点:
  1.委托是类型安全的
  委托类型的返回类型必须为void或者输出安全,委托类型的所有形参类型都必须是输⼊安全的。
  2.委托类型是名称等效,不是结构等效
  也就是说,对于两个委托类型,即使它们具有相同的参数列表和返回类型,它们仍将被视为两个不同的委托类型。
  例如:
  delegate int A(int x);
  delegate int B(int x);
  A和B是两个不同的委托。
  但是,两个结构⼀样的委托,它们的实例可以指向同⼀个⽅法。
  3.委托的调⽤列表(多播委托)
  委托实例所封装的⽅法集合称为调⽤列表。
  当我们从某个⽅法创建⼀个委托实例的时候,该实例将封装此⽅法,该实例中的调⽤列表包含⼀个“⼊⼝点”。当我们组合多个⾮空的委托实例的时候,它们的调⽤列表将连接在⼀起形成⼀个新的调⽤列表,新的调⽤列表中包含了多个“⼊⼝点”。
  委托的组合是使⽤⼆元运算符 + 和 += 来进⾏的,同样可以使⽤ - 和 -= 来进⾏组合的移除。
  下⾯的⽰例演⽰多个委托的实例化及其相应的调⽤列表:
delegate void D(int x)
class
{
public static void M1(int i){...}
public static void M2(int i){...}
}
class Test
{
static void Main()
{
D cd1 = new D(c.M1);            //M1
D cd2 = new D(c.M2);            //M2
D cd3 = cd1 + cd2;              //M1 + M2
D cd4 = cd3 + cd1;              //M1 + M2 + M1
D cd5 = cd4 + cd3;              //M1 + M2 + M1 + M2
}
}
实例化cd1和cd2的时候,它们分别封装⼀个⽅。实例化cd3的时候,它调⽤的列表有两个⽅法M1和M2,
⽽且顺序与此相同。cd4的调⽤列表中依次包含M1、M2、M1。最后,cd5的调⽤列表中依次包含M2、M1、M1、M2。
  4.委托的调⽤
  当调⽤⼀个委托实例的时候,将按照调⽤列表的顺序依次调⽤列表中的各个⽅法,当在调⽤期间发⽣异常,调⽤列表中排在后⾯的任何⽅法将不会被调⽤。
using System;
delegate void D(int x);
class C
{
public static void M1(int i)
{
Console.WriteLine("C.M1:"+i);
}
public static void M2(int i)
{
Console.WriteLine("C.M1:"+i);
public void M3(int i)
{
Console.WriteLine("C.M2:"+i);
}
}
class Test
{
static void Main()
{
D cd1 = new D(c.M1);            //M1
cd1(-1);                        //调⽤cd1
D cd2 = new D(c.M2);            //M2
cd2(-2);                        //调⽤M2
D cd3 = cd1 + cd2;              //M1 + M2
cd3(10);                        //依次调⽤M1、M2
cd3 += cd1;
cd3(20);                        //依次调⽤M1、M2、M1
C c = new C();
D cd4 = new D(c.M3);
cd3 += cd4;
cd3(30);                        //依次调⽤M1、M2、M1、M3
cd3 -= cd1                      //移除最后⼀个M1
cd3(40);                        //依次调⽤M1、M2、M3
cd3 -= cd4;
cd3(50);                        //依次调⽤M1、M2
cd3 -= cd2
cd3(60);                        //调⽤M1
cd3 -= cd2                      //这是cd3的调⽤列表中没有cd2了,该移除不⽣效,不报错
cd3(70);                        //调⽤M1
cd3 -= cd1
//      cd3(70);                //如果调⽤,将会报System.NullReferenceException异常
}
}
Lambda表达式
  ⾃从C#3.0开始,就可以使⽤⼀种新语法把实现代码赋予委托:Lambda表达式。只要有委托参数类型的地⽅,就可以使⽤Lambda表达式。使⽤匿名⽅法的地⽅可以使⽤Lambda表达式来代替,例如:
Func< string,string > = delagate(string para)
{
para += "Hello World!";
return param;
}
//可以写成
Func< string,string > = para=>
{
para +="Hello World!";
return para;
}
  Lambda表达式运算符”=>”的左边列出了需要的参数,右边定义了赋予Lambda变量的⽅法的代码实现。
  我们注意到,⽆论何时,只要我们需要引⼊匿名⽅法,我们都需要在前⾯加上delegate关键字,⽽且参数列表都需要类型化。Lambda 表达式提供了⼀种更为简洁和⾃然的语法,⽽且在C#更多⾼级的⽅⾯中都涉及⼴泛。简⽽⾔之,我们应该⽤Lambda表达式来替代匿名⽅法。
  例如:
public class Person
{
string Name;
int Age;
}
List<Person> personList = new List<Person>();
personList.Find(delegate(Person p){retrun p.Age==12;});
//可以写成
personList.Find(p=>p.Age==12);
表达式树
表达式树允许将 lambda 表达式表⽰为数据结构⽽⾮可执⾏代码。表达式⽬录树是System.Linq.Expressions.Expression< D > 形式的表达式⽬录树类型 (expression tree type) 的值,其中 D 是任何委托类型。
  如果存在从 lambda 表达式到委托类型 D 的转换,则也存在到表达式树类型 Expression< D > 的转换。⽽lambda 表达式到委托类型的转换⽣成引⽤该 lambda 表达式的可执⾏代码的委托,到表达式树类型的转换创建该 lambda 表达式的表达式树表⽰形式。
  表达式树是 lambda 表达式在内存中的⾼效数据表⽰形式,使 lambda 表达式的结构透明⽽⼜清晰。
writeline函数  与委托类型 D ⼀样, Expression< D > 具有与 D 相同的参数和返回类型。
  下⾯的⽰例将 lambda 表达式表⽰为可执⾏代码和表达式树。因为存在到 Func< int,int > 的转换,所以也存在到 Expression< Func< int,int > > 的转换:
  Func< int,int > del = x => x + 1; // Code
  Expression< Func< int,int > > exp = x => x + 1; // Data
  进⾏上⾯的赋值之后,委托 del 引⽤返回 x + 1 的⽅法,表达式⽬录树 exp 引⽤描述表达式 x => x +1 的数据结构。
  泛型类型 Expression< D > 的确切定义以及将 lambda 表达式转换为表达式树类型时有关构造表达式树的准确规则不在本⽂的介绍范围之内,将另作说明。
  有两个要点需要明确指出:
  ● 并⾮所有 lambda 表达式都能转换为表达式树。例如,具有语句体的 lambda 表达式和包含赋值表达式的 lambda 表达式不能这样表⽰。在这些情况下,转换仍存在,但在编译时将失败
  ● Expression< D > 提供⼀个实例⽅法 Compile,该⽅法产⽣⼀个类型为 D 的委托:
Func< int,int > del2 = exp.Compile();
  调⽤此委托将导致执⾏表达式树所表⽰的代码。因此,根据上⾯的定义, del 和 del2 等效,⽽且
  下⾯的两个语句也将等效:
  int i1 = del(1);
  int i2 = del2(1);
  执⾏此代码后, i1 和 i2 的值都为 2。
c#中有Expression,即表达式。
通过Expression可以动态构造代码,并编译执⾏。 ⽐如:
// 1.创建参数表达式:
ParameterExpression numParam = Expression.Parameter(typeof(int), "num");、
//创建常量表达式:
ConstantExpression five = Expression.Constant(5, typeof(int));
//创建⽐较表达式:
BinaryExpression numLessThanFive = Expression.LessThan(numParam, five);
//创建标号:
LabelTarget label = Expression.Label(typeof(int));
//创建循环或者分⽀表达式:
Expression.Loop(
// Adding a conditional block into the loop.
Expression.IfThenElse(
// Condition: value > 1
Expression.GreaterThan(value, Expression.Constant(1)),
// If true: result *= value --
Expression.MultiplyAssign(result,
Expression.PostDecrementAssign(value)),
// If false, exit the loop and go to the label.
Expression.Break(label, result)
),
/
/ Label to jump to.
label
)
//2.创建表达式树:
Expression<Func<int, bool>> exprTree = num => num < 5;
//3.分解表达式:
ParameterExpression param = (ParameterExpression)exprTree.Parameters[0];
//4.编译表达式:
Func<int, bool> result = expr.Compile();

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。