返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C#值类型、引用类型、泛型、集合、调用函数的表达式树实践
  • 164
分享到

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

2024-04-02 19:04:59 164人浏览 八月长安
摘要

目录一,定义变量二,访问变量/类型的属性字段和方法1. 访问属性调用静态类型属性调用实例属性/字段2. 调用函数调用静态类型的函数调用实例的函数三,实例化引用类型new给属性赋值创建

一,定义变量

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<Func<string>> lambda = Expression.Lambda<Func<string>>(member);

            string result = lambda.Compile()();
            Console.WriteLine(result);

            Console.ReadKey();

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

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

调用实例属性/字段

C#代码如下

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

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

            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() 方法

            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() 的重载方法比较多,常用的重载方法是

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<Action<Test>> lambda = Expression.Lambda<Action<Test>>(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<MemberBinding> 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<MemberBinding>() { }
                );

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

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

示例

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

            // 调用构造函数
            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 类,改成这样

    public class Test<T>
    {
        public void Print<T>(T info)
        {
            Console.WriteLine(info);
        }
    }

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

        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# 初始化一个集合并且添加元素,可以这样

            List<string> list = new List<string>()
            {
                "a",
                "b"
            };
            list.Add("666");

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

示例

            MethodInfo listAdd = typeof(List<string>).GetMethod("Add");

            
            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<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#值类型、引用类型、泛型、集合、调用函数的表达式树实践的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持编程网。

--结束END--

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

本文链接: https://lsjlt.com/news/162856.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • C#值类型、引用类型、泛型、集合、调用函数的表达式树实践
    目录一,定义变量二,访问变量/类型的属性字段和方法1. 访问属性调用静态类型属性调用实例属性/字段2. 调用函数调用静态类型的函数调用实例的函数三,实例化引用类型new给属性赋值创建...
    99+
    2024-04-02
  • C#值类型、引用类型、泛型、集合的表达式树怎么创建
    这篇文章主要介绍了C#值类型、引用类型、泛型、集合的表达式树怎么创建的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇C#值类型、引用类型、泛型、集合的表达式树怎么创建文章都会有所收获,下面我们一起来看看吧。一,定...
    99+
    2023-06-26
  • C#中值类型和引用类型的区别
    一、值类型和引用类型的区别 .NET的类型可以分为两类:值类型和引用类型。这两种类型各有特点,即使它们都继承自System.Object,并且有装箱和拆箱等操作确保两种类型可以方便地...
    99+
    2024-04-02
  • C++ 函数调用泛型编程:参数传递和返回值的类型抽象
    泛型编程可实现代码的类型抽象,包括参数传递和返回值类型抽象。参数传递抽象使用模板指定参数类型,如 template t sum(t a, t b),允许对不同类型进行求和。返回值抽象使用...
    99+
    2024-05-03
    c++ 泛型编程
  • C#怎么使用泛型集合存储不同类型的对象
    在C#中,您可以使用泛型集合来存储不同类型的对象。为此,您可以使用泛型类List或Dictionary<TKey, TValu...
    99+
    2024-04-09
    C#
  • c#引用类型与值类型的区别是什么
    这篇文章主要讲解了“c#引用类型与值类型的区别是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“c#引用类型与值类型的区别是什么”吧!解析:CLR支持两种类型:值类型和引用类型。用Jeff...
    99+
    2023-06-17
  • C++ 函数使用类型别名定义返回值类型
    问题: c++++中如何使用类型别名定义函数返回值类型?答案:使用 using 关键字声明类型别名,将原始类型命名为新类型别名。在函数签名中使用类型别名来指定返回值类型。使用类型别名可以...
    99+
    2024-04-14
    函数 类型别名 c++
  • C# 泛型集合类List<T>使用总结
    目录为什么选择使用List,而不是使用Array,或者ArryList去重、交集、并集、差集操作重写Equals() 和 GetHashCode()简单使用C#中List可谓是使用最...
    99+
    2024-04-02
  • C#泛型集合类List<T>如何使用
    这篇“C#泛型集合类List<T>如何使用”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“C#泛型集合类List&...
    99+
    2023-06-30
  • java泛型类型的调用和实例化介绍
    本篇内容主要讲解“java泛型类型的调用和实例化介绍”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“java泛型类型的调用和实例化介绍”吧!1、泛型调用类似于普通方法调用,但你不是把参数传递给方法...
    99+
    2023-06-20
  • C#中的值类型与引用类型有什么区别
    在C#中,值类型和引用类型是两种不同的数据类型,它们有以下区别: 存储位置:值类型的变量直接存储在堆栈中,而引用类型的变量存储在...
    99+
    2024-04-03
    C#
  • golang函数类型的广泛应用
    go语言中的函数类型允许将函数作为参数或返回值传递,实现灵活的代码。它包括:函数类型声明:func(*argtypes) returntype传递函数作为参数:将函数类型作为其他函数的参...
    99+
    2024-04-28
    函数 golang go语言
  • 使用类型修饰符定义 C++ 函数返回值类型
    c++++ 函数返回值类型使用类型修饰符指定,其中:void 表示没有返回值;int、float、double 等表示返回基本数据类型;引用类型 (&) 表示返回对数据的引用;指...
    99+
    2024-04-14
    java php 关键词:编程 c++
  • 泛型函数在Golang中如何处理指针和引用类型?
    泛型函数在 go 中处理指针类型时,会收到原始变量的引用,允许修改变量值。引用类型则在传递时会被复制,使函数无法修改原始变量值。实战案例包括使用泛型函数比较字符串或数字切片。 泛型函数...
    99+
    2024-04-16
    指针 引用类型 golang
  • C++ lambda 表达式的返回值类型如何定义?
    在 c++++ 中,lambda 表达式的返回值类型通过 ->return-type 指定,允许明确定义 lambda 的返回值。通过指定返回值类型,可以增强代码的可读性并避免编译器自动...
    99+
    2024-04-17
    c++ lambda
  • 使用指向类型参数的指针创建泛型类型的实例
    php小编鱼仔在这篇文章中将介绍如何使用指向类型参数的指针来创建泛型类型的实例。泛型是一种在编程中广泛应用的概念,它允许我们在不指定具体类型的情况下编写通用的代码。在php中,虽然没有...
    99+
    2024-02-09
  • C#基础入门之值类型和引用类型的区别详析
    目录一、值类型和引用类型的区别 1、赋值时的区别 2、内存分配的区别 3、来自继承结构的区别 二、总结 一、值类型和引用类型的区别 .NET的类型可以分为两类:值类型和引用类型。这...
    99+
    2024-04-02
  • C++ 函数中 lambda 表达式的类型推导机制是什麼?
    当 lambda 表达式从封闭作用域捕获变量时,返回类型将推导为捕获变量的类型。如果捕获了多个变量,则返回类型将由它们的类型共同推导。该机制允许在 lambda 表达式中推导出其返回类型...
    99+
    2024-04-25
    函数 lambda c++ 作用域
  • C++ 函数返回值的指南:类型、含义和最佳实践
    c++++ 函数可返回包括基本类型、派生类型、void、引用和指针在内的各种数据类型。函数返回值含义因上下文而异,但通常表示计算结果、执行状态、对内部数据结构的引用。最佳实践包括选择合适...
    99+
    2024-05-03
    c++ 函数返回值 质数
  • Go函数返回值类型推断的最佳实践
    在 go 中,使用类型推断最佳实践如下:显式指定显而易见或关键的类型。对于简单的函数,使用类型推断。使用不同包时注意类型推断。在接口和泛型函数中显式指定类型。 Go 函数返回值类型推断...
    99+
    2024-04-29
    go 返回值类型推断
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作