c#值类型、引用类型、泛型、集合、调用函数的表达式树实践-kb88凯时官网登录

来自:网络
时间:2023-01-01
阅读:
免费资源网 - https://freexyz.cn/
目录

一,定义变量

c# 表达式树中,定义一个变量,使用 parameterexpression

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

expression.parameter()
expression.variable()
// 另外,定义一个常量可以使用 expression.constant()。

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

对于使用定义:

expression.variable 用于在块内声明局部变量。

expression.parameter用于声明输入值的参数。

先看第一种

        public static parameterexpression parameter(type type)
        {
            return parameter(type, name: null);
        }
        
                public static parameterexpression variable(type type)
        {
            return variable(type, name: null);
        }

从代码来看,没有区别。

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

        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);
        }
        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

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

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

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

expression.property()

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

expression.propertyorfield()

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

expression.call()

访问属性字段和方法

expression.makememberaccess

他们都返回一个 memberexpression类型。

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

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

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

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

1. 访问属性

使用 expression.property() 或 expression.propertyorfield()调用属性。

调用静态类型属性

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

            console.writeline(console.title);

使用表达式树表达如下

            memberexpression member = expression.property(null, typeof(console).getproperty("title"));
            expression> lambda = expression.lambda>(member);
            string result = lambda.compile()();
            console.writeline(result);
            console.readkey();

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

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

调用实例属性/字段

c#代码如下

            list a = new list() { 1, 2, 3 };
            int result = a.count;
            console.writeline(result);
            console.readkey();

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

            parameterexpression a = expression.parameter(typeof(list), "a");
            memberexpression member = expression.property(a, "count");
            expression, int>> lambda = expression.lambda, int>>(member, a);
            int result = lambda.compile()(new list { 1, 2, 3 });
            console.writeline(result);
            console.readkey();

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

2. 调用函数

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

调用静态类型的函数

以 console 为例,调用 writeline() 方法

            console.writeline("调用writeline方法");
            methodcallexpression method = expression.call(
                null,
                typeof(console).getmethod("writeline", new type[] { typeof(string) }),
                expression.constant("调用writeline方法"));
            expression lambda = expression.lambda(method);
            lambda.compile()();
            console.readkey();

expression.call() 的重载方法比较多,常用的重载方法是

public static methodcallexpression call(expression instance, methodinfo method, params expression[] arguments)

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

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

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

调用实例的函数

写一个类

    public class test
    {
        public void print(string info)
        {
            console.writeline(info);
        }
    }

调用实例的 printf() 方法

            test test = new test();
            test.print("打印出来");
            console.readkey();

表达式表达如下

            parameterexpression a = expression.variable(typeof(test), "test");
            methodcallexpression method = expression.call(
                a,
                typeof(test).getmethod("print", new type[] { typeof(string) }),
                expression.constant("打印出来")
                );
            expression> lambda = expression.lambda>(method,a);
            lambda.compile()(new test());
            console.readkey();

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

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

三,实例化引用类型

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

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

new

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

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

 public static newexpression new(constructorinfo constructor);
 public static newexpression new(type type);

依然使用上面的 test 类型

            newexpression newa = expression.new(typeof(test));

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

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

            newexpression newa = expression.new(typeof(test).getconstructor(xxxxxx));

这里就不详细说了。

给属性赋值

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

        public static memberinitexpression memberinit(newexpression newexpression, ienumerable bindings);
        public static memberinitexpression memberinit(newexpression newexpression, params memberbinding[] bindings);

两种重载是一样的。

我们将 test 类改成

    public class test
    {
        public int sample { get; set; }
        public void print(string info)
        {
            console.writeline(info);
        }
    }

然后

            var binding = expression.bind(
                typeof(test).getmember("sample")[0],
                expression.constant(10)
            );

创建引用类型

expression.memberinit()

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

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

            newexpression newa = expression.new(typeof(test));
            memberinitexpression test = expression.memberinit(newa,
                new list() { }
                );

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

            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}
                );

示例

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

            // 调用构造函数
            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() { binding }
                );
            // 调用方法
            methodcallexpression method1 = expression.call(
                test,
                typeof(test).getmethod("print", new type[] { typeof(string) }),
                expression.constant("打印出来")
                );
            // 调用属性
            memberexpression method2 = expression.property(test, "sample");
            expression lambda1 = expression.lambda(method1);
            lambda1.compile()();
            expression> lambda2 = expression.lambda>(method2);
            int sample = lambda2.compile()();
            console.writeline(sample);
            console.readkey();

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

将 test 类,改成这样

    public class test
    {
        public void print(t info)
        {
            console.writeline(info);
        }
    }

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

        static void main(string[] args)
        {
            runexpression();
            console.readkey();
        }
        public static void runexpression()
        {
            // 调用构造函数
            newexpression newa = expression.new(typeof(test));
            // 实例化一个类型
            memberinitexpression test = expression.memberinit(newa,
                new list() { }
                );
            // 调用方法
            methodcallexpression method = expression.call(
                test,
                typeof(test).getmethod("print").makegenericmethod(new type[] { typeof(t) }),
                expression.constant("打印出来")
                );
            expression lambda1 = expression.lambda(method);
            lambda1.compile()();
            console.readkey();
        }

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

集合类型使用 listinitexpression表示。

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

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

listinit 初始化一个集合。

c# 中,集合都实现了 ienumerable,集合都具有 add 扥方法或属性。

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

            list list = new list()
            {
                "a",
                "b"
            };
            list.add("666");

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

示例

            methodinfo listadd = typeof(list).getmethod("add");
            /*
             * new list()
             * {
             *     "a",
             *     "b"
             * };
             */
            elementinit add1 = expression.elementinit(
                listadd,
                expression.constant("a"),
                expression.constant("b")
                );
            // add("666")
            elementinit add2 = expression.elementinit(listadd, expression.constant("666"));

示例

            methodinfo listadd = typeof(list).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));
            // 初始化值
            listinitexpression setlist = expression.listinit(
                list,
                add1,
                add2,
                add3
                );
            // 没啥执行的,就这样看看输出的信息
            console.writeline(setlist.tostring());
            memberexpression member = expression.property(setlist, "count");
            expression> lambda = expression.lambda>(member);
            int result = lambda.compile()();
            console.writeline(result);
            console.readkey();

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

免费资源网 - https://freexyz.cn/
返回顶部
顶部
网站地图