服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - C#值类型、引用类型、泛型、集合、调用函数的表达式树实践

C#值类型、引用类型、泛型、集合、调用函数的表达式树实践

2022-12-20 15:02痴者工良 C#

本文详细讲解了C#值类型、引用类型、泛型、集合、调用函数的表达式树实践,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

一,定义变量

C# 表达式树中,定义一个变量,使用 ParameterExpression

创建变量结点的方法有两种,

?
1
2
3
Expression.Parameter()
Expression.Variable()
// 另外,定义一个常量可以使用 Expression.Constant()。

两种方式都是生成 ParameterExpression 类型 Parameter() 和 Variable() 都具有两个重载。他们创建一个 ParameterExpression节点,该节点可用于标识表达式树中的参数或变量。

对于使用定义:

Expression.Variable 用于在块内声明局部变量。

Expression.Parameter用于声明输入值的参数。

先看第一种

?
1
2
3
4
5
6
7
8
9
public static ParameterExpression Parameter(Type type)
{
    return Parameter(type, name: null);
}
 
        public static ParameterExpression Variable(Type type)
{
    return Variable(type, name: null);
}

从代码来看,没有区别。

再看看具有两个参数的重载

?
1
2
3
4
5
6
7
8
9
10
11
public static ParameterExpression Parameter(Type type, string name)
{
    Validate(type, allowByRef: true);
    bool byref = type.IsByRef;
    if (byref)
    {
        type = type.GetElementType();
    }
 
    return ParameterExpression.Make(type, name, byref);
}
?
1
2
3
4
5
public static ParameterExpression Variable(Type type, string name)
{
    Validate(type, allowByRef: false);
    return ParameterExpression.Make(type, name, isByRef: false);
}

如你所见,两者只有一个 allowByRef 出现了区别,Paramter 允许 Ref, Variable 不允许。

笔者在官方文档和其他作者文章上,都没有找到具体区别是啥,去 stackoverflow 搜索和查看源代码后,确定他们的区别在于 Variable 不能使用 ref 类型。

从字面意思来看,声明一个变量,应该用Expression.Variable, 函数的传入参数应该使用Expression.Parameter

无论值类型还是引用类型,都是这样子定义。

二,访问变量/类型的属性字段和方法

访问变量或类型的属性,使用

?
1
Expression.Property()

访问变量/类型的属性或字段,使用

?
1
Expression.PropertyOrField()

访问变量或类型的方法,使用

?
1
Expression.Call()

访问属性字段和方法

?
1
Expression.MakeMemberAccess

他们都返回一个 MemberExpression类型。

使用上,根据实例化/不实例化,有个小区别,上面说了变量或类型。

意思是,已经定义的值类型或实例化的引用类型,是变量;

类型,就是指引用类型,不需要实例化的静态类型或者静态属性字段/方法。

上面的解释不太严谨,下面示例会慢慢解释。

1. 访问属性

使用 Expression.Property() 或 Expression.PropertyOrField()调用属性。

调用静态类型属性

Console 是一个静态类型,Console.Title 可以获取编译器程序的实际位置。

?
1
Console.WriteLine(Console.Title);

使用表达式树表达如下

?
1
2
3
4
5
6
7
MemberExpression member = Expression.Property(null, typeof(Console).GetProperty("Title"));
Expression<Func<string>> lambda = Expression.Lambda<Func<string>>(member);
 
string result = lambda.Compile()();
Console.WriteLine(result);
 
Console.ReadKey();

因为调用的是静态类型的属性,所以第一个参数为空。

第二个参数是一个 PropertyInfo 类型。

调用实例属性/字段

C#代码如下

?
1
2
3
4
List<int> a = new List<int>() { 1, 2, 3 };
int result = a.Count;
Console.WriteLine(result);
Console.ReadKey();

在表达式树,调用实例的属性

?
1
2
3
4
5
6
7
8
ParameterExpression a = Expression.Parameter(typeof(List<int>), "a");
MemberExpression member = Expression.Property(a, "Count");
 
Expression<Func<List<int>, int>> lambda = Expression.Lambda<Func<List<int>, int>>(member, a);
int result = lambda.Compile()(new List<int> { 1, 2, 3 });
Console.WriteLine(result);
 
Console.ReadKey();

除了 Expression.Property() ,其他的方式请自行测试,这里不再赘述。

2. 调用函数

使用 Expression.Call() 可以调用一个静态类型的函数或者实例的函数。

调用静态类型的函数

以 Console 为例,调用 WriteLine() 方法

?
1
2
3
4
5
6
7
8
9
10
Console.WriteLine("调用WriteLine方法");
 
MethodCallExpression method = Expression.Call(
    null,
    typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }),
    Expression.Constant("调用WriteLine方法"));
 
Expression<Action> lambda = Expression.Lambda<Action>(method);
lambda.Compile()();
Console.ReadKey();

Expression.Call() 的重载方法比较多,常用的重载方法是

?
1
public static MethodCallExpression Call(Expression instance, MethodInfo method, params Expression[] arguments)

因为要调用静态类型的函数,所以第一个 instance 为空(instance英文意思是实例)。

第二个 method 是要调用的重载方法。

最后一个 arguments 是传入的参数。

调用实例的函数

写一个类

?
1
2
3
4
5
6
7
public class Test
{
    public void Print(string info)
    {
        Console.WriteLine(info);
    }
}

调用实例的 Printf() 方法

?
1
2
3
Test test = new Test();
test.Print("打印出来");
Console.ReadKey();

表达式表达如下

?
1
2
3
4
5
6
7
8
9
10
11
ParameterExpression a = Expression.Variable(typeof(Test), "test");
 
MethodCallExpression method = Expression.Call(
    a,
    typeof(Test).GetMethod("Print", new Type[] { typeof(string) }),
    Expression.Constant("打印出来")
    );
 
Expression<Action<Test>> lambda = Expression.Lambda<Action<Test>>(method,a);
lambda.Compile()(new Test());
Console.ReadKey();

注意的是,Expression.Variable(typeof(Test), "test"); 仅定义了一个变量,还没有初始化/赋值。对于引用类型来说,需要实例化。

上面的方式,是通过外界实例化传入里面的,后面会说如何在表达式内实例化。

三,实例化引用类型

引用类型的实例化,使用 new ,然后选择调用合适的构造函数、设置属性的值。

那么,根据上面的步骤,我们分开讨论。

new

使用 Expression.New()来调用一个类型的构造函数。

他有五个重载,有两种常用重载:

?
1
2
public static NewExpression New(ConstructorInfo constructor);
public static NewExpression New(Type type);

依然使用上面的 Test 类型

?
1
NewExpression newA = Expression.New(typeof(Test));

默认没有参数的构造函数,或者只有一个构造函数,像上面这样调用。

如果像指定一个构造函数,可以

?
1
NewExpression newA = Expression.New(typeof(Test).GetConstructor(xxxxxx));

这里就不详细说了。

给属性赋值

实例化一个构造函数的同时,可以给属性赋值。

?
1
2
3
public static MemberInitExpression MemberInit(NewExpression newExpression, IEnumerable<MemberBinding> bindings);
 
public static MemberInitExpression MemberInit(NewExpression newExpression, params MemberBinding[] bindings);

两种重载是一样的。

我们将 Test 类改成

?
1
2
3
4
5
6
7
8
public class Test
{
    public int sample { get; set; }
    public void Print(string info)
    {
        Console.WriteLine(info);
    }
}

然后

?
1
2
3
4
var binding = Expression.Bind(
    typeof(Test).GetMember("sample")[0],
    Expression.Constant(10)
);

创建引用类型

?
1
Expression.MemberInit()

表示调用构造函数并初始化新对象的一个或多个成员。

如果实例化一个类,可以使用

?
1
2
3
4
NewExpression newA = Expression.New(typeof(Test));
MemberInitExpression test = Expression.MemberInit(newA,
    new List<MemberBinding>() { }
    );

如果要在实例化时给成员赋值

?
1
2
3
4
5
6
7
8
9
NewExpression newA = Expression.New(typeof(Test));
 
// 给 Test 类型的一个成员赋值
var binding = Expression.Bind(
    typeof(Test).GetMember("sample")[0],Expression.Constant(10));
 
MemberInitExpression test = Expression.MemberInit(newA,
    new List&lt;MemberBinding&gt;() { binding}
    );

示例

实例化一个类型,调用构造函数、给成员赋值,示例代码如下

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 调用构造函数
NewExpression newA = Expression.New(typeof(Test));
 
// 给 Test 类型的一个成员赋值
var binding = Expression.Bind(
    typeof(Test).GetMember("sample")[0], Expression.Constant(10));
 
// 实例化一个类型
MemberInitExpression test = Expression.MemberInit(newA,
    new List<MemberBinding>() { binding }
    );
 
// 调用方法
MethodCallExpression method1 = Expression.Call(
    test,
    typeof(Test).GetMethod("Print", new Type[] { typeof(string) }),
    Expression.Constant("打印出来")
    );
 
// 调用属性
MemberExpression method2 = Expression.Property(test, "sample");
 
Expression<Action> lambda1 = Expression.Lambda<Action>(method1);
lambda1.Compile()();
 
Expression<Func<int>> lambda2 = Expression.Lambda<Func<int>>(method2);
int sample = lambda2.Compile()();
Console.WriteLine(sample);
 
Console.ReadKey();

四,实例化泛型类型于调用

将 Test 类,改成这样

?
1
2
3
4
5
6
7
public class Test<T>
{
    public void Print<T>(T info)
    {
        Console.WriteLine(info);
    }
}

Test 类已经是一个泛型类,表达式实例化示例

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
static void Main(string[] args)
{
    RunExpression<string>();
    Console.ReadKey();
}
public static void RunExpression<T>()
{
    // 调用构造函数
    NewExpression newA = Expression.New(typeof(Test<T>));
 
    // 实例化一个类型
    MemberInitExpression test = Expression.MemberInit(newA,
        new List<MemberBinding>() { }
        );
 
    // 调用方法
    MethodCallExpression method = Expression.Call(
        test,
        typeof(Test<T>).GetMethod("Print").MakeGenericMethod(new Type[] { typeof(T) }),
        Expression.Constant("打印出来")
        );
 
    Expression<Action> lambda1 = Expression.Lambda<Action>(method);
    lambda1.Compile()();
 
    Console.ReadKey();
}

五,定义集合变量、初始化、添加元素

集合类型使用 ListInitExpression表示。

创建集合类型,需要使用到

ElementInit 表示 IEnumerable集合的单个元素的初始值设定项。

ListInit 初始化一个集合。

C# 中,集合都实现了 IEnumerable,集合都具有 Add 扥方法或属性。

使用 C# 初始化一个集合并且添加元素,可以这样

?
1
2
3
4
5
6
List<string> list = new List<string>()
{
    "a",
    "b"
};
list.Add("666");

而在表达式树里面,是通过 ElementInit 调用 Add 方法初始化/添加元素的。

示例

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
MethodInfo listAdd = typeof(List<string>).GetMethod("Add");
 
/*
 * new List<string>()
 * {
 *     "a",
 *     "b"
 * };
 */
ElementInit add1 = Expression.ElementInit(
    listAdd,
    Expression.Constant("a"),
    Expression.Constant("b")
    );
// Add("666")
ElementInit add2 = Expression.ElementInit(listAdd, Expression.Constant("666"));

示例

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
MethodInfo listAdd = typeof(List<string>).GetMethod("Add");
 
ElementInit add1 = Expression.ElementInit(listAdd, Expression.Constant("a"));
ElementInit add2 = Expression.ElementInit(listAdd, Expression.Constant("b"));
ElementInit add3 = Expression.ElementInit(listAdd, Expression.Constant("666"));
 
NewExpression list = Expression.New(typeof(List<string>));
 
// 初始化值
ListInitExpression setList = Expression.ListInit(
    list,
    add1,
    add2,
    add3
    );
// 没啥执行的,就这样看看输出的信息
Console.WriteLine(setList.ToString());
 
MemberExpression member = Expression.Property(setList, "Count");
 
Expression<Func<int>> lambda = Expression.Lambda<Func<int>>(member);
int result = lambda.Compile()();
Console.WriteLine(result);
 
Console.ReadKey();

 到此这篇关于C#值类型、引用类型、泛型、集合、调用函数的表达式树实践的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://www.cnblogs.com/whuanle/p/11569083.html

延伸 · 阅读

精彩推荐
  • C#Unity shader实现自由放大缩小效果

    Unity shader实现自由放大缩小效果

    这篇文章主要为大家详细介绍了Unity shader实现自由放大缩小效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    clzmin9992022-03-11
  • C#C#裁剪,缩放,清晰度,水印处理操作示例

    C#裁剪,缩放,清晰度,水印处理操作示例

    这篇文章主要为大家详细介绍了C#裁剪,缩放,清晰度,水印处理操作示例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    吴 剑7442021-12-08
  • C#C#计算字符串哈希值(MD5、SHA)的方法小结

    C#计算字符串哈希值(MD5、SHA)的方法小结

    这篇文章主要介绍了C#计算字符串哈希值(MD5、SHA)的方法,以实例形式较为详细的分析总结了C#计算字符串哈希值的各种常用技巧,具有一定参考借鉴价值...

    北风其凉8042021-10-19
  • C#同步调用和异步调用WebService

    同步调用和异步调用WebService

    本文给大家介绍webservice同步调用和异步调用,同步调用就是一个同步操作会阻塞整个当前的进程,直到这个操作完成才能执行下一段代码,异步调用不会阻...

    C#教程网5252021-10-28
  • C#c#中CAD文件读取实例

    c#中CAD文件读取实例

    在本篇文章里小编给大家整理的是一篇关于c#中CAD文件读取实例内容,有兴趣的朋友们可以学习参考下。...

    sky41010852022-11-20
  • C#C#多线程传递参数及任务用法示例

    C#多线程传递参数及任务用法示例

    这篇文章主要介绍了C#多线程传递参数及任务用法,结合简单实例形式分析了C#多线程的使用及相关的参数传递与任务创建等使用技巧,需要的朋友可以参考下...

    smartsmile201210722021-11-25
  • C#C#实现类似jQuery的方法连缀功能

    C#实现类似jQuery的方法连缀功能

    这篇文章主要介绍了C#实现类似jQuery的方法连缀功能,可以简化语句,使代码变得清晰简单,感兴趣的小伙伴们可以参考一下...

    JackWang-CUMT4762021-11-02
  • C#C#实现简单飞行棋小游戏

    C#实现简单飞行棋小游戏

    这篇文章主要为大家详细介绍了C#实现简单飞行棋小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    再学一个我就睡10022022-11-08