相关文章推荐
迷茫的手术刀  ·  Java-“this”和“类名.this”以 ...·  1 月前    · 
聪明伶俐的单杠  ·  SuperMap iObjects ...·  2 周前    · 
寂寞的登山鞋  ·  Springboot动态生成网站地图site ...·  2 周前    · 
玩足球的炒粉  ·  时间序列预测(商品销量预测) - 石头木 ·  1 周前    · 
憨厚的火腿肠  ·  判断字符串是否相等_判断两个字符串是否相等 ...·  5 天前    · 
千年单身的开水瓶  ·  CSS ...·  1 年前    · 
耍酷的柑橘  ·  国家互联网信息办公室关于《移动互联网未成年人 ...·  1 年前    · 
仗义的冲锋衣  ·  什么是非基元反应?非基元反应的概念是什么?·  2 年前    · 
憨厚的烤红薯  ·  解决uniapp获取base64在ios端自 ...·  2 年前    · 
Code  ›  C#进阶学习--反射(Reflection)开发者社区
string
https://cloud.tencent.com/developer/article/1933474
旅行中的蛋挞
2 年前
作者头像
腾云大使
0 篇文章

C#进阶学习--反射(Reflection)

原创
前往专栏
腾讯云
开发者社区
文档 意见反馈 控制台
首页
学习
活动
专区
工具
TVP
文章/答案/技术大牛
发布
首页
学习
活动
专区
工具
TVP
返回腾讯云官网
社区首页 > 专栏 > 用户9161392的专栏 > C#进阶学习--反射(Reflection)

C#进阶学习--反射(Reflection)

原创
作者头像
腾云大使
发布 于 2022-01-13 09:04:12
666 0
发布 于 2022-01-13 09:04:12
举报

一.反射的定义

审查元数据并收集关于它的类型信息的能力。

二.基础概念

(1)Assembly:定义和加载程序集,加载在程序集中的所有模块以及从此程序集中查找类型并创建该类型的实例。

(2)Module:获取包含模块的程序集以及模块中的类等,还可以获取在模块上定义的所有全局方法或其他特定的非全局方法。

(3)ConstructorInfo:获取构造函数的名称、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。

(4)MethodInfo(GetMethod/GetMethods):获取方法的名称、返回类型、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。

(5)FiedInfo(GetField/GetFields):获取字段的名称、访问修饰符(如public或private)和实现详细信息(如static)等,并获取或设置字段值。

(6)EventInfo(GetEvent/GetEvents):获取事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等,添加或移除事件处理程序。

(7)PropertyInfo(GetProperty/GetProperties):获取属性的名称、数据类型、声明类型、反射类型和只读或可写状态等,获取或设置属性值。

(8)ParameterInfo:获取参数的名称、数据类型、是输入参数还是输出参数,以及参数在方法签名中的位置等。

(9)MemberInfo(GetMember/GetMembers):获取字段、事件、属性等各种信息

三.反射作用

在演示反射的作用之前,我们先定义如下实体类,假设该实体类位于一个第三方的类库下,类库名称为“TestClass”,类名为"Person"

    public class Person
        private int id;
        public int Id { get => id; set => id = value; }
        public string Name { set; get; }
        public string Phone { get; set; }      
        public Person()
        public Person(string a, int b)
            this.Name = a;
            this.Phone = b.ToString();
        public Person(int id, string name, string phone)
            this.Id = id;
            this.Name = name;
            this.Phone = phone;
        public string getName()
            return this.Name;            
        public string getName1(string str)
            return str;
        public string getPhone()
            return this.Phone;
        public string getPhone(string str)
            return this.Phone+str;
        public string getPhone(string str,int num)
            return this.Phone + str+num;
        private void privateMethod(string a)
            Console.WriteLine("这是一个私有方法,传入的参数是:"+a);
    }

1.创建不带参数的对象

创建不带成熟的对象,本质是就是调用无参的构造函数,具体实现如下

        /// <summary>
        /// 创建不带参数的对象
        /// </summary>
        /// <returns></returns>
        private static Person CreateWithoutParms()
            Assembly assembly = Assembly.Load("TestClass");//加载程序集
            Type type = assembly.GetType("TestClass.Person");//获取类名称(要带上命名空间)
            object o = Activator.CreateInstance(type);//创建Person实体,无参构造
            Person person = o as Person;
            return person;
        }

在控制台中调用

            Person person = CreateWithoutParms();
            person.Name = "张三";
            Console.WriteLine("person.Name:"+ person.Name);
返回结果如下:

成功调用了创建了Person,并调用了Person的无参构造方法

2.创建带参数的对象

创建带成熟的对象,本质是就是调用带参数的构造函数,具体实现如下

        /// <summary>
        /// 创建带参数的对象
        /// </summary>
        /// <returns></returns>
        private static Person CreateWithParms()
            Assembly assembly = Assembly.Load("TestClass");//加载程序集
            Type type = assembly.GetType("TestClass.Person");//获取类名称(要带上命名空间)
            object o = Activator.CreateInstance(type, new object[] {"a",666 });//创建Person实体,有参构造
            Person person = o as Person;
            return person;
        }

在控制台中调用

            Person person = CreateWithParms();
            Console.WriteLine("person.Name:"+person.Name+ " person.Phone:" + person.Phone);
返回结果如下:

成功调用了创建了Person,并利用带参数的构造直接给属性赋值

####说明:如果构造函数为私有的,可以在创建实例时,将CreateInstance中的nonPublic参数设置为true,即可使用私有的构造函数创建实例

            object o = Activator.CreateInstance(type,true);

3.调用公共方法

利用反射调用第三方类的方法,可以通过反射得到对应的对象之后,利用得到的对象来执行对象中的方法,但是在这里,主要讲解通过反射,直接调用第三方类中的方法,具体实现如下

        /// <summary>
        /// 调用带参数的方法(无重载)
        /// </summary>
        /// <returns></returns>
        private static string CallFunction()
            Assembly assembly= Assembly.Load("TestClass");
            Type type = assembly.GetType("TestClass.Person");
            object o = Activator.CreateInstance(type);
            MethodInfo methodInfo = type.GetMethod("getName1");
            string result=methodInfo.Invoke(o, new object[] { "这是传入参数" }).ToString();
            return result;
        }

在控制台中调用

            string rsult = CallFunction();
            Console.WriteLine(rsult);
返回结果如下:

在这里我们看到,利用反射成功调用了getName1方法,需要注意的是,getName1方法并没有任何重载,如果需要调用带有重载的方法,需要用下面的方法,这里我们假设需要调用getPhone(string str,int num)方法

        private static string CallFunctionWithOverload()
            Assembly assembly = Assembly.Load("TestClass");
            Type type = assembly.GetType("TestClass.Person");
            object o = Activator.CreateInstance(type);
            MethodInfo methodInfo = type.GetMethod("getPhone", new Type[] { typeof(string), typeof(int) });//在这里需要把参数类型数组传递给GetMethod方法
            string result=methodInfo.Invoke(o, new object[] { "这是传入的String参数", 666 }).ToString();
            return result;
        }

在控制台中调用

            string rsult = CallFunctionWithOverload();
            Console.WriteLine(rsult);
返回结果如下:
通过以上的例子,我们可以看到,调用有重载和无重载方法的关键,就是在GetMethod中是否传递参数的类型。

下面写一个综合的例子,调用Person类中的所有方法,并输出结果,如果参数类型为String,则默认传"AAA",如果参数类型为Int,则默认传666,实现方法如下:

        private static void CallAllFunction()
            Assembly assembly = Assembly.Load("TestClass");
            Type type = assembly.GetType("TestClass.Person");
            object o = Activator.CreateInstance(type);
            foreach (MethodInfo methodInfoItem in type.GetMethods())
                Console.WriteLine("执行"+ methodInfoItem.Name+ "方法");
                List<object> objectList = new List<object>();
                foreach (ParameterInfo parameterInfoItem in methodInfoItem.GetParameters())
                    if (parameterInfoItem.ParameterType == typeof(String))
                        objectList.Add("AAA");
                    else if (parameterInfoItem.ParameterType == typeof(int))
                        objectList.Add(666);
                try//这里使用try...catch...是为了简化处理属性获取失败导致程序报错问题
                    string result = methodInfoItem.Invoke(o, objectList.ToArray()).ToString();
                    Console.WriteLine("结果为:" + result);
                catch 
        }

调用后返回结果如下:

在这里我们看到,Person中的方法已经全部执行,包括所有的系统方法

4.调用私有方法

        /// <summary>
        /// 调用私有方法
        /// </summary>
        private static void CallPrivateFunction()
            Assembly assembly = Assembly.Load("TestClass");
            Type type = assembly.GetType("TestClass.Person");
            object o = Activator.CreateInstance(type);
            MethodInfo methodInfo = type.GetMethod("privateMethod", BindingFlags.Instance | BindingFlags.NonPublic);
            methodInfo.Invoke(o, new object[] { "张三"});
        }
调用后返回结果如下:
通过以上例子,我们不难发现,调用公共方法与私有方法的区别就是在调用type的GetMethod方法时,是否设置"BindingFlags.Instance | BindingFlags.NonPublic"

5.获取与操作属性

        /// <summary>
        /// 获取与操作属性
        /// </summary>
        /// <param name="propertyName"></param>
        /// <param name="propertyValue"></param>
        private static void getAndSetProperity(string propertyName,string propertyValue)
            //1.通过反射创建Person实体
            Assembly assembly = Assembly.Load("TestClass");
            Type type = assembly.GetType("TestClass.Person");
            object o = Activator.CreateInstance(type, new object[] { "张三", 131000000 });
            PropertyInfo propertyInfo=type.GetProperty(propertyName);
            Console.WriteLine("修改前Phone:"+ propertyInfo.GetValue(o));//获取属性值
            propertyInfo.SetValue(o, propertyValue);//设置属性值
            Console.WriteLine("修改后Phone:" + propertyInfo.GetValue(o));           
        }
调用后返回结果如下:
通过以上例子,可以发现,获取与设置属性的关键方法分别为GetValue与SetValue,关键传入参数为通过反射得到的实体类

6.获取与操作字段

        /// <summary>
        /// 获取与操作字段
        /// </summary>
        /// <param name="fieldName"></param>
        /// <param name="fieldValue"></param>
        private static void getAndSetField(string fieldName, int fieldValue)
            Assembly assembly = Assembly.Load("TestClass");
            Type type = assembly.GetType("TestClass.Person");
            object o = Activator.CreateInstance(type, new object[] {1, "张三", "131000000" });            
            FieldInfo fieldInfo = type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
            Console.WriteLine("修改前id"+ fieldInfo.GetValue(o));
            fieldInfo.SetValue(o, fieldValue);
            Console.WriteLine("修改后id" + fieldInfo.GetValue(o));
        }
调用后返回结果如下:

设置和操作字段的方法与设置和操作属性的方法基本一直,需要注意的是,在用type的GetField方法时,如果获取或设置的是私有字段,需要设置该方法的可访问属性,本例中的设置为"BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance"

接下来,我们继续研究反射在泛型中的作用,在进一步研究之前,我们先定义如下泛型类,同以上实体类一样,假设该泛型类位于一个第三方的类库下,类库名称为“TestClass”,类名为"GenericClass"

    public class GenericClass<X,Y,Z>
        public X xxx{ set; get; }
        public Y yyy { set; get; }
        public Z zzz { set; get; }
        public void PrintParm<A,B,C>(A a, B b, C c)
            Console.WriteLine("A的类型为" + a.GetType().Name + ",值为" + a.ToString());
            Console.WriteLine("B的类型为" + b.GetType().Name + ",值为" + b.ToString());
            Console.WriteLine("C的类型为" + c.GetType().Name + ",值为" + c.ToString());
    }

7.创建泛型类并调用

        /// <summary>
        /// 调用泛型类中的方法
        /// </summary>
        private static void GenericWithParms()
            Assembly assembly = Assembly.Load("TestClass"); 
            Type type = assembly.GetType("TestClass.GenericClass`3"); 
 
推荐文章
迷茫的手术刀  ·  Java-“this”和“类名.this”以及“类名.class”的区分和详解开发者社区
1 月前
聪明伶俐的单杠  ·  SuperMap iObjects Java 之多任务生成地图瓦片_iobject java 创建地图
2 周前
寂寞的登山鞋  ·  Springboot动态生成网站地图sitemap教程开发者社区
2 周前
玩足球的炒粉  ·  时间序列预测(商品销量预测) - 石头木
1 周前
憨厚的火腿肠  ·  判断字符串是否相等_判断两个字符串是否相等 fortran
5 天前
千年单身的开水瓶  ·  CSS transform中的rotate的旋转中心怎么设置 - CSDN文库
1 年前
耍酷的柑橘  ·  国家互联网信息办公室关于《移动互联网未成年人模式建设指南 ...
1 年前
仗义的冲锋衣  ·  什么是非基元反应?非基元反应的概念是什么?
2 年前
憨厚的烤红薯  ·  解决uniapp获取base64在ios端自带前缀(不显示)问题_js ios无效base64_前端bug工程师的博客-CSDN博客
2 年前
今天看啥   ·   Py中国   ·   codingpro   ·   小百科   ·   link之家   ·   卧龙AI搜索
删除内容请联系邮箱 2879853325@qq.com
Code - 代码工具平台
© 2024 ~ 沪ICP备11025650号