C#-委托和表达式树
委托概述#
c# 内置的几种数据类型中就有委托,可见其重要。
传递参数时可以传值类型,引用类型。那方法可不可以作为一个参数传递呢?委托就是实现这个功能
委托脑图#
委托实例和声明#
- 可以声明在类外部,也可以声明在外部;
- 没有方法体,只有分号结尾;
- 修饰符可以是new,public,private,protecte,internal
public delegate void DoProcess(string msg);
委托组合删除#
void Main()
{
var s02=new S02_Delegate();
DoProcess p1,p2,p3,p4;
p1=s02.process_one;
p2=s02.process_two;
//实例化
var msg="100块";
p1(msg);
p2(msg);
//组合
p3=p1+p2;
p3(msg);
//删除
p3-=p1;
p3(msg);
}
public delegate void DoProcess(string msg);
public class S02_Delegate
{
public void process_one(string msg)
{
Console.WriteLine("process one:{0}", msg);
}
public void process_two(string msg)
{
Console.WriteLine("process two:{0}", msg);
}
}
结果:
process one:100块
process two:100块
process one:100块
process two:100块
process two:100块
System.Fun和System.Action#
System.Action
: 用于无返回值得方法;System.Fun
: 用于有返回值的方法;
System.Fun泛型委托,可以没有输入参数,但必须有返回值。根据输入参数的多少有17个重载
public delegate TResult Func<out TResult>();//无输入参数,有返回值。
public delegate TResult Func<in T,out TResult>(T arg);//有输入参数,有返回值
public delegate TResult Func<in T1,in T2,out TResult>(T1 arg1, T2 arg2);
...
Action泛型委托根据输入参数个数的不同,Action委托有十六个重载:
public delegate void Action();//无参数无返回值委托
public delegate void Action<in T>(T obj);//泛型委托,无返回值
public delegate void Action<in T1,in T2>(T1 arg1, T2 arg2);
...
匿名方法#
匿名方法是没有方法名的方法块,方法只能声明后使用,匿名方法使用时才定义,所以系统开销要小一些。而且当你传递给一个委托一个方法又不想单独定义时,匿名方法可以简单实现
定义很简单:将方法名换成delegate即可。
使用规则也要注意:
- 不能使用跳转语句跳至方法外部;
- 方法内部不能访问不安全密码;
public delegate void MyDelegate1();
public delegate int MyDelegate2();
public delegate void MyDelegate3(string msg,bool iSuccess);
void Main()
{
MyDelegate1 d1 = delegate { Console.WriteLine("无参匿名");};
MyDelegate2 d2 = delegate { return 100;};
MyDelegate3 d3 = delegate(string msg,bool iSuccess) { Console.WriteLine(msg); };
}
lamabda 表达式#
lamabda 表达式是简写的匿名方法,语法如下
(参数列表)=>{语句}
要是语句是一个代码块可以叫语句lamabda,要是语句只有一个return语句构成叫表达式lamabda。
//语句lamabda
System.Func<string> fun1 =() =>
{
string input;
do
{
input = "end";
} while (input != "end");
return input;
}
//表达式lamabda
System.Func<int,int,bool> fun2=(a,b)=> a==b;
表达式树#
为什么需要表达式树#
表达式树是将我们原来可以直接由代码编写的逻辑以表达式的方式存储在树状的结构里,从而可以在运行时去解析这个树,然后执行,实现动态的编辑和执行代码。LINQ to SQL就是通过把表达式树翻译成SQL来实现的
官方怎么解释#
表达式树是将代码以一种抽象的方式表示成一个对象树,树中每个节点本身都是一个表达式(只能是表达式)。表达式树不是可执行代码,它是一种数据结构。System.Linq.Expressions命名空间中包含了代表表达式的各个类用以实现这种数据结构。
什么样的算表达式呢?以下这些都是C#表达式:
3 //常数表达式
a //变量或参数表达式
!a //一元逻辑非表达式
a + b //二元加法表达式
Math.Sin(a) //方法调用表达式
new StringBuilder() //new 表达式
这些表达式是预编译好的,运行过程中无法修改,但通过Expression等类可以实现动态修改代码。真正做到“代码作为数据”。
创建表达式树#
表达式:num => num == 0 如何表示
Expression静态方法构建表达式树#
ParameterExpression pExpression = Expression.Parameter(typeof(int)); //参数:num
ConstantExpression cExpression = Expression.Constant(0); //常量:0
BinaryExpression bExpression = Expression.MakeBinary(ExpressionType.Equal, pExpression, cExpression); //表达式:num == 0
Expression<Func<int, bool>> lambda = Expression.Lambda<Func<int, bool>>(bExpression, pExpression); //lambda 表达式:num => num == 0
Lambda表达式构转换成达式树#
通过 Expression
Expression<Func<int, bool>> funcExpression1 = (num) => num== 0;
编译和执行表达式树#
表达式树是不能执行的,只有将其编译委托才能执行。
Expression
var fun1 = lambda.Compile();
Console.WriteLine(fun1(0));//True
Console.WriteLine(lambda.Compile()(0));//True
表达式树图结构#
Expression<Func<int , int , bool>> additionExpressionInfo = ( x , y ) => x != y && x != 0;
面例子中创建了一个表达式( x , y ) => x != y && x!=0,代码体是x != y && x!=0,&&是一个表达式,它含有左右两个操作数的子表达式,所以它会被拆分,左边x != y是一个表达式,右边 x!=0也是一个表达式,左右两边都含有子表达式,所以会继续拆分,直到无法拆分为止,结构如下:
Expression各种类#
树的每个子节点是一个具体的表达式,它们都有自己的表达式类型,这些类型从Expression派生。
- UnaryExpression:一元运算表达式
- BinaryExpression:二元运算表达式
- ConstantExpression:常量表达式
- ParameterExpression:变量、变量参数表达式
- GotoExpression:跳转语句表达式,如:return。continue、break
- BlockExpression:块语句表达式
- ConditionalExpression:条件语句表达式
- LoopExpression:循环语句表达式
- SwitchExpression:选择语句表达式
- IndexExpression:访问数组索引表达式
- MethodCallExpression:调用方法表达式
- LambdaExpression:Lambda表达式
- TypeBinaryExpression:类型检查表达式
- NewArrayExpression: 创建数组表达式
- DefaultExpression:默认值表达式
- DynamicExpression:动态类型表达式
- TryExpression:try语句表达式
- MemberExpression:类成员表达式
- InvocationExpression:执行Lambda并传递实参的表达式
- NewExpression:调用无参构造函数表达式
- MemberInitExpression:调用带参构造函数表达式,可初始化成员
- ListInitExpression:集合初始化器表达式
构建各式各样的表达式树#
构建以下表达式树:
-a
a + b * 2
Math.Sin(x) + Math.Cos(y)
new StringBuilder(“Hello”)
new int[] { a, b, a + b}
a[i – 1] * i
a.Length > b | b >= 0
new System.Windows.Point() { X = Math.Sin(a), Y = Math.Cos(a) }
void Main()
{
ParameterExpression expADouble = Expression.Parameter(typeof(double), "a");
ParameterExpression expBDouble = Expression.Parameter(typeof(double), "b");
ParameterExpression expAInt = Expression.Parameter(typeof(int), "a");
ParameterExpression expBInt = Expression.Parameter(typeof(int), "b");
ParameterExpression expAArray = Expression.Parameter(typeof(int[]), "a");
ParameterExpression expIInt = Expression.Parameter(typeof(int), "i");
ParameterExpression expAString = Expression.Parameter(typeof(string), "a");
//-a
var g1 = Expression.Negate(expADouble);
Console.WriteLine(g1.ToString());
//a + b * 2
var g2 = Expression.Add(expAInt, Expression.Multiply(expBInt, Expression.Constant(2)));
Console.WriteLine(g2.ToString());
var g2result = Expression.Lambda<Func<int, int, int>>(g2, expAInt, expBInt).Compile();
//Assert.AreEqual(7, g2result(3, 2));
//Math.Sin(x) + Math.Cos(y)
var g3 = Expression.Add(Expression.Call(null, typeof(Math).GetMethod("Sin", BindingFlags.Public | BindingFlags.Static), expADouble),
Expression.Call(null, typeof(Math).GetMethod("Cos", BindingFlags.Public | BindingFlags.Static), expBDouble));
Console.WriteLine(g3.ToString());
//new StringBuilder(“Hello”)
var g4 = Expression.New(typeof(StringBuilder).GetConstructor(new Type[] { typeof(string) }), Expression.Constant("Hello"));
Console.WriteLine(g4.ToString());
//new int[] { a, b, a + b}
var g5 = Expression.NewArrayInit(typeof(int), expAInt, expBInt, Expression.Add(expAInt, expBInt));
Console.WriteLine(g5.ToString());
//a[i – 1] * i
var g6 = Expression.Multiply(Expression.ArrayAccess(expAArray, Expression.Subtract(expIInt, Expression.Constant(1))), expIInt);
Console.WriteLine( g6.ToString());
//a.Length > b | b >= 0
var g7 = Expression.Or(Expression.GreaterThan(Expression.Property(expAString, "Length"), expBInt), Expression.GreaterThanOrEqual(expBInt, Expression.Constant(0)));
Console.WriteLine(g7.ToString());
// var g7Result = Expression.Lambda<Func<string, int, bool>>(g7, expAString, expBInt).Compile();
// Assert.AreEqual(true, g7Result("rico", 2));
//new System.Windows.Point() { X = Math.Sin(a), Y = Math.Cos(a) }
var g8 = Expression.MemberInit(
Expression.New(typeof(Point)), new MemberBinding[]{
Expression.Bind(typeof(Point).GetProperty("X"),Expression.Call(null, typeof(Math).GetMethod("Sin", BindingFlags.Public | BindingFlags.Static), expADouble)),
Expression.Bind(typeof(Point).GetProperty("Y"), Expression.Convert(Expression.Call(null, typeof(Math).GetMethod("Cos", BindingFlags.Public | BindingFlags.Static), expADouble),typeof(int)))
});
Console.WriteLine(g8.ToString());
}
public class Point
{
public double X { get; set; }
public int Y { get; set; }
}
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步