C#反射详解

2023-03-26 20:47:12

程序集

程序集是经由编译器编译得到的,供进一步编译执行的中间产物。
在Windows系统中,它一般表现为后缀为.dll(库文件)或者.exe(可执行文件)的格式。

元数据

元数据是用来描述数据的数据。即程序中的类、类中的函数、变量等信息就是程序的元数据。有关程序以及类型的数据被称为元数据,他们保存在程序集中。

反射的概念

程序正在运行时,可以查看其它程序集或者自身的元数据。
一个运行的程序查看本身或者其它程序的元数据的行为叫做反射。
说的通俗点就是在程序运行时,通过反射可以得到其它程序集或者自己程序集代码的各种信息,包括类、函数、变量等来实例化它们,执行它们,操作它们。

反射的作用

因为反射可以在程序编译后获得信息,所以它提高了程序的拓展性和灵活性。
1、程序运行时得到所有元数据,包括元数据的特性。
2、程序运行时实例化对象,操作对象。
3、程序运行时创建新的对象,用这些对象执行任务。

语法

C#反射的语法主要包括三个类:分别是Type、Assembly和Activator。
首先创建一个测试类方便测试:

class Test
    {
        private int i = 1;
        public int j = 0;
        public string str = "123";
        public Test()
        {

        }
        public Test(int i)
        {
            this.i = i;
        }
        public Test(int i ,string str) : this(i)
        {
            this.str = str;
        }

        public void Speak()
        {
            Console.WriteLine(i);
        }
    }

Type

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

获取Type

得到类的程序集信息

1、object中的GetType()可以获取对象的Type

            int a = 42;
            Type type = a.GetType();
            Console.WriteLine(type);

打印结果是System.Int32即为a的类型

2、通过typeof关键字,传入类名可以获取对象的Type

            Type type2 = typeof(int);
            Console.WriteLine(type2);

打印结果是System.Int32即为int的类型

3、通过类的名字可以获取类型,这里要注意类名必须包含明明空间,否则会找不到。

            Type type3 = Type.GetType("Int32");
            Console.WriteLine(type3);
            Type type4 = Type.GetType("System.Int32");
            Console.WriteLine(type4);

打印结果type3为空,type4为System.Int32

得到类的程序集信息

可以通过Type得到类型所在程序集信息。

            Console.WriteLine(type.Assembly);
            Console.WriteLine(type2.Assembly);
            Console.WriteLine(type4.Assembly);

获取类中的所有公共成员

需要引用命名空间using System.Reflection;
1、首先得到Type

            Type t = typeof(Test);

这里有一个小的知识点,通过typeof获取Type一般是在能够明确得到类名的情况下,即获取同一程序集下的类的Type的时候;如果是不同程序集下的类,可以通过Type.GetType()获取。

2、然后得到所有公共成员

            MemberInfo[] infos = t.GetMembers();
            for (int i = 0; i < infos.Length; i++)
            {
                Console.WriteLine(infos[i]);
            }

MemberInfo即该类型下公共成员容器。

获取类的公共构造函数并调用

1、获取所有构造函数
获取构造函数可以通过GetConstructor和GetConstructors获得,通过ConstructorInfo接收。

            ConstructorInfo[] ctors = t.GetConstructors();
            for (int i = 0; i < ctors.Length; i++)
            {
                Console.WriteLine(ctors[i]);
            }

2、获取其中一个构造函数并执行。
2.1:得构造函数需要传入Type数组,数组中内容按顺序是参数类型。
2.2:执行构造函数,需要传入object数组,表示按顺序传入的参数。
无参构造函数:

            //得到无参构造函数
            ConstructorInfo info = t.GetConstructor(new Type[0]);
            //执行无参构造 无参构造没有参数传null
            Test obj = info.Invoke(null) as Test;
            Console.WriteLine(obj.j);

有参构造函数:

            //得到有参构造函数
            ConstructorInfo info2 = t.GetConstructor(new Type[] { typeof(int), typeof(string) });
            //执行有参构造函数
            obj = info2.Invoke(new object[] { 2 ,"asd"}) as Test;
            Console.WriteLine(obj.str);

获取类的公共成员变量

1、得到所有成员变量

            FieldInfo[] fileldInfos = t.GetFields();
            for (int i = 0; i < fileldInfos.Length; i++)
            {
                Console.WriteLine(fileldInfos[i]);
            }

2、得到指定名称的公共成员变量

            FieldInfo infoJ = t.GetField("j");
            Console.WriteLine(infoJ);

3、通过反射获取和设置对象的值
获取和设置可以分别通过GetValue和SetValue进行,这里我们使用上面得到的obj来测试。
3.1、通过反射获取对象的某个变量的值

            Console.WriteLine(infoJ.GetValue(obj));

3.2、通过反射设置指定对象的某个变量的值

            infoJ.SetValue(obj, 100);
            Console.WriteLine(infoJ.GetValue(obj));

获取类的公共成员方法

获取成员方法可以通过Type类中的GetMethod方法来得到类中的方法,MethodInfo是方法的反射信息。
如果存在方法重载,用Type数组表示参数类型。
这里为了方便测试,我们使用string类来查看里面的方法。

            Type strType = typeof(string);
            //得到所有函数
            MethodInfo[] methods = strType.GetMethods();
            for (int i = 0; i < methods.Length; i++)
            {
                Console.WriteLine(methods[i]);
            }
            //得到指定的Substring函数(int,int)重载
            MethodInfo method = strType.GetMethod("Substring", new Type[] { typeof(int), typeof(int) });
            Console.WriteLine(method);

调用方法(注意:如果是静态方法,Invoke中的第一个参数传null即可)

            string str = "Hello,World!";
            //第一个参数相当于哪个对象要执行这个成员方法,
            object result = method.Invoke(str, new object[] { 7, 5 });
            Console.WriteLine(result);

其他

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

Activator

Activator是用于快速实例化对象的类,用于将Type对象快捷实例化为对象。
先得到Type,然后快速实例化一个对象。
Activator.CreateInstance默认调用无参构造函数。
1、无参构造函数:

            Type test = typeof(Test);
            Test testObj = Activator.CreateInstance(test) as Test;
            Console.WriteLine(testObj.str);

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

            testObj = Activator.CreateInstance(test, 99) as Test;
            testObj = Activator.CreateInstance(test, 55, "11111") as Test;
            Console.WriteLine(testObj.str);

Assembly

Assembly类其实就是程序集类。主要用来加载其他程序集,加载后才能用Type来使用其他程序集中的信息,如果想要使用不是自己程序集中的内容,需要先加载程序集(比如dll文件)。
三种加载程序集的函数:
1、一般用来加载同一文件下的其他程序集
Assembly assembly = Assembly.Load(“程序集名称”);

2、一般用来加载不再同一文件下的其他程序集
Assembly assembly = Assembly.LoadFrom(“包含程序集清单的文件的名称或路径”);
Assembly assembly = Assembly.LoadFile(“要加载的文件的完全限定路径”);

使用方法:
1、首先加载一个指定程序集:

            Assembly assembly = Assembly.LoadFrom (@"C:\Users\01\Desktop\ConsoleApp1\TestDLL\bin\Debug\TestDLL.dll");
            Type[] types = assembly.GetTypes();
            for (int i = 0; i < types.Length; i++)
            {
                Console.WriteLine(types[i]);
            }

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

其中TestDLL.dll为自己封装的dll,内容如下:

namespace TestDLL
{
    public class Class1
    {
        public int i = 0;
        public string str = "123";
        public void Speak()
        {
            Console.WriteLine("speak");
        }

        
    }
}
  • 作者:Master-Xue
  • 原文链接:https://blog.csdn.net/qq_39029746/article/details/115233295
    更新时间:2023-03-26 20:47:12