(2/∞)C#中的反射机制

在认识C# 反射之前,需要先认识几个概念

程序集(Assembly) 程序集是经由编译器得到的,供给CLR进一步编译的中间产物,在Windows系统中一般表现为.dll(库文件) 和 .exe(可执行文件)类似如下

 元数据(Metadata)通俗来说,元数据就是用来描述数据的数据,在程序中的类、类中的函数、变量等等都是程序的元数据,元数据被保存在程序集中。

在了解了这两个概念后,可以稍微理解什么是反射

程序在运行时 可以查看其他的程序集或者自身的元数据,一个运行的程序查看本身或者其他程序的元数据的行为就叫做反射。

那么C#中是如何实现反射的?

C#提供了一个特殊的类,Type,可以把Type看做类的信息类,它是反射的基础,我们可以使用Type去后去有关类型的成员(如构造函数、方法、字段、属性、类的事件)

存在三种方法获取Type:

1.通过调用Object基类中的GetType方法去获去Type

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

2.通过typeof关键字传入类名(最常使用)

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

3.通过类的名字去获取

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

上面三行代码均打印出这个信息,Type类型的名称

在得到了Type类型后,可以去获取到类型所在程序集的信息

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

 同样,三行代码打印出来相同的信息,int类型所在的程序集的信息

 继续,既然得到了Type类型,我们也能够去获得到类中的所有公共成员

1.获取类的构造函数并调用

            //1.获取所有的公共构造函数
            ConstructorInfo[] ctors = t.GetConstructors();
            for (int i = 0; i < ctors.Length; i++)
            {
            Console.WriteLine(ctors[i]);
            }

            //2.获取其中一个构造函数 并执行
            //得构造函数 Type数组 数组中内容按顺序是 构造函数的参数类型
            //执行构造函数传入 object数组 表示按顺序传入参数内容

            //2-1 得到无参构造
            ConstructorInfo info = t.GetConstructor(new Type[0]);
            //执行无参构造
            Test obj = info.Invoke(null) as Test;
            Console.WriteLine(obj.j);

            //2-2 得到有参构造
            ConstructorInfo info2 = t.GetConstructor(new Type[] { typeof(int) });
            obj = info2.Invoke(new object[] { 2 }) as Test;
            Console.WriteLine(obj.str);

            ConstructorInfo info3 = t.GetConstructor(new Type[] { typeof(int), typeof(string) });
            obj = info3.Invoke(new object[] { 2, "反射捏" }) as Test;
            Console.WriteLine(obj.j+obj.str);
            Console.WriteLine("------------------------------------");

2.获取类的公共变量成员

            //1.得到所有的成员变量
            FieldInfo[] fieldInfo = t.GetFields();
            for (int i = 0; i < fieldInfo.Length; i++)
            {
                Console.WriteLine(fieldInfo[i]);
            }

            //2.得到指定名称的公共成员变量
            FieldInfo infoJ = t.GetField("j");
            Console.WriteLine(infoJ);

            //3.通过反射 获取和设置对象的值
            Test test = new Test();
            test.j = 99;
            test.str = "222";
            //3-1 通过反射 获取对象的某个变量的值
            Console.WriteLine(infoJ.GetValue(test));
            //3-2 通过反射 设置指定对象的某个变量的值
            infoJ.SetValue(test,101);
            Console.WriteLine(infoJ.GetValue(test));

3.获取类中的公共成员方法

            //通过Type类中的GetMethod方法得到类中的方法
            // MethodInfo 是方法的反射信息
            Console.WriteLine("-----------------------------------");
            Type strType = typeof(string);
            MethodInfo[] methodInfos = strType.GetMethods();
            for (int i = 0; i < methodInfos.Length; i++)
            {
                Console.WriteLine(methodInfos[i]);
            }
            //1.如果方法存在重载 用Type数组表示参数类型
            MethodInfo methodInfo =strType.GetMethod("Substring",new Type[]{ typeof(int),typeof(int) });
            //2.调用该方法
            string str = "Kojima Hedio";
            //第一个参数 是相当于那个对象要执行这个成员方法  
            object result = methodInfo.Invoke(str, new object[] { 0, 6 });  
            Console.WriteLine(result);
            //注意:如果是静态方法 Invoke中的第一个参数传入null即可

我们在上面获取到了类型所在的程序集,其本质也是一个程序集类,它的主要目的是用来加载其他的程序集,才能用Type来使用其他程序集中的信息。如果想要使用不是自己的程序集就要先加载程序集。C#提供了三种加载程序集的方法。

            //一般用来加载在同一文件下的其它程序集
            Assembly asembly2 = Assembly.Load("程序集名称");

            //一般用来加载不在同一文件下的其它程序集
            Assembly asembly = Assembly.LoadFrom("包含程序集清单的文件的名称或路径");
            Assembly asembly3 = Assembly.LoadFile("要加载的文件的完全限定路径"); 

在我们获取了其他程序集之后,进行各种反射操作例如下面:

            //1.先加载一个指定的程序集
            Assembly assembly = Assembly.LoadFrom(@"C:\Users\墨晴轩\source\repos\多线程练习\bin\Debug\net5.0\多线程练习");
            Type[] types = assembly.GetTypes();
            Console.WriteLine("-----------------------------------");
            for (int i = 0; i < types.Length; i++)
            {
                Console.WriteLine(types[i]);
            }
            //2.在加载程序集中的一个类对象 之后才能使用反射
            Type icon = assembly.GetType("多线程练习.Icon");
            MemberInfo[] memberInfos = icon.GetMembers();
            for (int i = 0; i < memberInfos.Length; i++)
            {
                Console.WriteLine(memberInfos[i]);
            }
            //通过反射实例化一个 icon 对象

            //首先得到枚举Type 得到可以传入的参数
            Type moveDir = assembly.GetType("多线程练习.E_MoveDir");
            FieldInfo right = moveDir.GetField("Right");

            //实例化对象
            object iconObj = Activator.CreateInstance(icon, 10, 5, right);

            //通过反射得到对象中的方法
            MethodInfo move = icon.GetMethod("Move");
            MethodInfo draw = icon.GetMethod("Draw");
            MethodInfo clear = icon.GetMethod("Clear");
            Console.Clear();
            while (true)
            {
                Thread.Sleep(1000);
                clear.Invoke(iconObj, null);
                move.Invoke(iconObj, null);
                draw.Invoke(iconObj, null);
            }

在上面的代码中,我们通过Type去实例化了一个对象。由此可见,C#也给我们提供在反射中用于快速创建的类,Activator

            //用于快速实例化对象的类
            // 用于将Type 对象快捷实例化为对象
            //先得到Type 
            //然后 快速实例化一个对象
            Type testType = typeof(Test);
            //1.无参构造
            Test testObj = Activator.CreateInstance(testType) as Test ;
            Console.WriteLine(testObj.str);

            //有参构造
            testObj = Activator.CreateInstance(testType, 99) as Test;
            testObj = Activator.CreateInstance(testType, 99, "123123") as Test;

猜你喜欢

转载自blog.csdn.net/qq_52690206/article/details/127045297