C# 反射(Reflection)总结

目录

一,什么是反射?

二,为什么使用反射?

2.1 反射机制的优缺点

三,如何使用反射?

3.1 Type访问元数据

获取/修改类中公有成员(属性PropertyI和字段Field等)

调用类中的公有构造函数Constructor

调用类中的公有方法Method

 获取类中的私有成员(包括方法)

3.2 Activator快速实例化对象

3.3 Assembly加载程序集


一,什么是反射?

反射是.NET Framework 的功能,并不是C#语言特有的功能。简单理解来看就是:

给我一个对象,我能在不用new操作符的情况下,也不知道是什么静态类型的情况下,创建一个同类型的对象,同时还能访问这个对象的各个成员。(在获取其他实体类的字段名或实列,只能获取公有的,而有了反射之后可以获取私有的,可以获取他的基类等等,可以说把家底查得清清楚楚。)

二,为什么使用反射?

很多时候程序的逻辑并不是在写的时候就能确定,有时需要用户交互时才确定,但此时程序已经运行。如果要程序员在静态编写时,去枚举用户可能做的操作,会让程序变得十分臃肿,可读性、可维护性都很烂,并且枚举用户可能做的操作这件事是很难实现的。这是我们需要的这种:以不变应万变的能力,就是反射机制。

通过反射在运行时取得一些编译期不存在的类型的信息,创建实例,或者调用方法。

2.1 反射机制的优缺点

优点

  • 1、反射提高了程序的灵活性和扩展性。
  • 2、降低耦合性,提高自适应能力。
  • 3、它允许程序创建和控制任何类的对象,无需提前硬编码目标类。

缺点

  • 1、性能问题:反射是去内存中动态拿到对象/类型描述,再用这些描述去创建对象,这个过程是对性能有影响的(使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此反射机制主要应用在对灵活性和拓展性要求很高的系统框架上,普通程序不建议使用。)
  • 2、使用反射会模糊程序内部逻辑;程序员希望在源代码中看到程序的逻辑,反射却绕过了源代码的技术,因而会带来维护的问题,反射代码比相应的直接代码更复杂。

三,如何使用反射?

通过反射可以动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性,反射的语法主要通过三个类来实现:分别是Type、Assembly和Activator。

首先创建一个测试类Students方便测试:

    class Students
    {
        public string Name { get; set; }
        public int Age { get; set; }
        //公有字段
        public int a = 0;
        //私有属性
        private int weight { get; set; }
        public Students(string name,int age)
        {
            this.Name = name;
            this.Age = age;
        }
        public void message()
        {
            Console.WriteLine($"同学:{this.Name}已注册");
        }
    }

3.1 Type访问元数据

Type是类的信息类,它是反射功能的挤出,访问元数据的主要方式。
使用Type的成员获取有关类型(如构造函数、方法、字段、属性和类的事件等)声明的信息,具体步骤如下:

(1)获取Type,得到Students类的程序集信息,获取Type对象有两种方法:

//第一种:类未实例化(没用对象)
Type type = typeof(Students);

//第二种:类已实例化(new构建对象)
Students s= new Students();
Type studentsType = s.GetType();

 (2)获取Students类中的所有的信息

获取/修改类中公有成员(属性PropertyI和字段Field等)

首先变量类型声明方式创建一个 Type 类型的变量 type,表示 Students类型。接着,使用反射 API 获取 Student 类型的所有公共成员(属性和字段(变量))等信息,使用 foreach 遍历每个成员信息对象。

由于属性信息(PropertyInfo)和字段信息(FieldInfo)的基类都是 MemberInfo,因此可以通过  GetValue(Object obj)SetValue(Object obj,Object value)对单个公有成员进行获取/设置。

            Students student = new Students("李华",23);
            Type type = student.GetType();
            //获取属性
            {
                //方式一(获取类中全部属性)
                PropertyInfo[] studentType = type.GetProperties();
                //方式二(获取类中指定属性)
                var property = type.GetProperty("Age");
                foreach (var stu in studentType)
                {
                    //获取属性值
                    object obj = stu.GetValue(student);
                    Console.WriteLine($"{obj}");
                }
                //修改Age属性值为50
                property.SetValue(student, 50);
                Console.WriteLine($"修改后Age的值:{property.GetValue(student)}");
            }
            //获取字段(修改字段值和上面写法一致)
            {
                FieldInfo[] fieldInfo = type.GetFields();
                //获取变量a的类型
                var filed = type.GetField("a");
            }

获取其他公有成员变量大同小异,这里只提示方法并不一一赘述。
1、得到枚举:GetEnumName、GetEnumNames
2、得到事件:GetEvent、GetEvents
3、得到接口:GetInterface、GetInterfaces

调用类中的公有构造函数Constructor

获取类中的公有构造函数可以通过GetConstructor和GetConstructors获得,通过ConstructorInfo接收(或使用var类型),通过Invoke函数实例化构造函数。最后通过实例化对象可间接调用对象的方法以及对象的成员信息。

            //获取构造函数
            {
                //(一)获取无参构造函数
                ConstructorInfo info = type.GetConstructor(new Type[0]);
                //执行无参构造 无参构造没有参数传null
                Students obj = info.Invoke(null) as Students;
                //调用对象的方法
                obj.message();
                //(二)获取有参数构造函数
                ConstructorInfo info2 = type.GetConstructor(new Type[] { typeof(string), typeof(int) });
                //执行有参构造函数
                obj = info2.Invoke(new object[] { "小红", 11 }) as Students;
                obj.message();
            }

调用类中的公有方法Method

获取成员方法可以通过Type类中的GetMethod方法来得到类中的方法,MethodInfo是方法的反射信息。最后通过Invoke函数进行调用如果存在方法重载,用Type数组表示参数类型。

需要注意:当调用的是静态方法时,Invoke函数的第一个参数为null。

                var method = type.GetMethods();
                foreach (var item in method)
                {
                    //Invoke(object obj,object value)
                    item.Invoke(student,null);
                }
                //打印结果:
                //同学:李华已注册

 获取类中的私有成员(包括方法)

反射最牛的地方就是它可以获取私有方法与实列:

(1)BindingFlags.Instance 表示只获取实例方法

(2)BindingFlags.NonPublic 表示只获取非公共方法

(3)BindingFlags.Public 表示只获取公共方法

将测试类Students的成员变量修改为私有属性(private),再通过反射获取类中的私有成员时,需要设置获取成员API的BindingFlags参数,以上文获取类中属性为例:

           Students student = new Students("李华",23);
            Type type = student.GetType();
            //获取属性
            {
                //方式一(获取类中全部属性)
                PropertyInfo[] studentType = type.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic);
                //方式二(获取类中指定属性)
                var property = type.GetProperty("Age",BindingFlags.Instance | BindingFlags.NonPublic);
                foreach (var stu in studentType)
                {
                    //获取属性值
                    object obj = stu.GetValue(student);
                    Console.WriteLine($"{obj}");
                }
                //修改Age属性值为50
                property.SetValue(student, 50);
                Console.WriteLine($"修改后Age的值:{property.GetValue(student)}");
            }

3.2 Activator快速实例化对象

Activator用于实例化对象的类,通过type获取类型后,可以通过它实例化对象。Activator.CreateInstance默认调用无参构造函数。

1、无参构造函数:

            Type stu= typeof(Students);
            //快速实例化Students对象testObj
            Test testObj = Activator.CreateInstance(stu) as Students;

2、有参构造函数:

如果要调用有参构造函数,在后面一次添加参数即可。

            testObj = Activator.CreateInstance("小黄", 29) as Students;

3.3 Assembly加载程序集

Assembly类其实就是程序集类。主要用来加载其他程序集,加载后才能用Type来使用其他程序集中的信息,如果想要使用不是自己程序集中的内容,需要先加载程序集(比如dll文件)。

三种加载程序集的函数:

▲一般用来加载本地目录下的其他程序集

Assembly assembly = Assembly.Load(“程序集名称”);

▲一般用来加载不在本地目录下的其他程序集

Assembly assembly = Assembly.LoadFrom(“包含程序集清单的文件的名称或路径”);
Assembly assembly = Assembly.LoadFile(“要加载的文件的完全限定路径”);

使用方法:

1、首先加载一个指定程序集:

            Assembly assembly = Assembly.LoadFrom (@"C:\Users\TestDLL.dll");

2、再加载程序集中的一个类对象,之后才能使用反射

            //得到dll中的Class1类
            Type c = assembly.GetType("TestDLL.Class1");
            object o = Activator.CreateInstance(c);
            //调用Class1类中的Speak方法
            MethodInfo speak = c.GetMethod("Speak");
            speak.Invoke(o,null);

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C# 反射是指在运行时动态地获取类的信息,通过反射可以获取类的属性、方法、事件等信息,以及动态创建对象、调用方法、获取值等操作。这使得我们能够在运行时动态地编写代码,可以写出更加灵活和可扩展的程序。 C# 反射的核心是 `System.Reflection` 命名空间,该命名空间包含了许多与反射相关的类和接口,比如 `Type` 类、`MethodInfo` 类、`PropertyInfo` 类等。 下面是一些常用的反射操作: 1. 获取类型信息 可以使用 `typeof` 关键字或者 `Type.GetType()` 方法来获取类型的信息。`typeof` 关键字用于获取编译时已知的类型信息,而 `Type.GetType()` 方法则可以通过类型名称获取运行时的类型信息。 ```csharp // 获取 System.String 类型的信息 Type type1 = typeof(System.String); // 获取指定类型名称的信息 Type type2 = Type.GetType("System.String"); ``` 2. 获取成员信息 可以使用 `Type.GetMembers()` 方法获取类型的所有成员信息,包括属性、方法、字段、事件等。也可以使用 `Type.GetMethod()`、`Type.GetProperty()`、`Type.GetField()`、`Type.GetEvent()` 等方法获取指定成员的信息。 ```csharp Type type = typeof(Person); // 获取类型的所有成员信息 MemberInfo[] members = type.GetMembers(); // 获取指定属性的信息 PropertyInfo property = type.GetProperty("Name"); // 获取指定方法的信息 MethodInfo method = type.GetMethod("SayHello"); // 获取指定字段的信息 FieldInfo field = type.GetField("Age"); // 获取指定事件的信息 EventInfo eventInfo = type.GetEvent("PropertyChanged"); ``` 3. 动态创建对象 可以使用 `Activator.CreateInstance()` 方法动态创建对象,也可以使用 `Type.InvokeMember()` 方法调用构造函数来创建对象。 ```csharp Type type = typeof(Person); // 使用 Activator.CreateInstance() 方法创建对象 Person person1 = (Person)Activator.CreateInstance(type); // 使用 Type.InvokeMember() 方法调用构造函数创建对象 Person person2 = (Person)type.InvokeMember(null, BindingFlags.CreateInstance, null, null, new object[] { "Tom", 18 }); ``` 4. 调用成员 可以使用 `MethodInfo.Invoke()` 方法调用方法,也可以使用 `PropertyInfo.SetValue()` 方法设置属性的值,使用 `FieldInfo.SetValue()` 方法设置字段的值。 ```csharp Type type = typeof(Person); Person person = new Person("Tom", 18); // 调用方法 MethodInfo method = type.GetMethod("SayHello"); method.Invoke(person, null); // 设置属性的值 PropertyInfo property = type.GetProperty("Name"); property.SetValue(person, "Jerry", null); // 设置字段的值 FieldInfo field = type.GetField("Age"); field.SetValue(person, 20); ``` 以上是 C# 反射的一些基本操作,反射的应用非常广泛,可以用来实现插件式开发、ORM 映射等功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值