C# 反射 Reflection

2022/2/7 17:13:19

本文主要是介绍C# 反射 Reflection,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

参考资料:

C#反射机制 - 知乎 (zhihu.com)

一、基本概念

.Net的应用程序由几个部分:‘程序集(Assembly)’、‘模块(Module)’、‘类型(class)’组成,而反射提供一种编程的方式,让程序员可以在程序运行期获得这几个组成部分的相关信息。

 

Assembly——可以获得正在运行的装配件信息,也可以动态的加载装配件,以及在装配件中查找类型信息,并创建该类型的实例。

装配件是.Net应用程序执行的最小单位,编译出来的.dll、.exe都是装配件。

 

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


Type——可以获得对象的类型信息,此信息包含对象的所有要素:方法、构造器、属性等等,通过Type类可以得到这些要素的信息,并且调用之。

 

ConstructorInfo——了解构造函数的名称、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等


MethodInfo——包含方法的信息,通过这个类可以得到方法的名称、参数、返回值等,并且可以调用之。


EventInfo——了解事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等,添加或移除事件处理程序。


PropertyInfo——了解属性的名称、数据类型、声明类型、反射类型和只读或可写状态等,获取或设置属性值。


ParameterInfo——了解参数的名称、数据类型、是输入参数还是输出参数,以及参数在方法签名中的位置等。

 

.NET中的重要机制,通过反射,可以在运行时获得程序或程序集中每一个类型(包括类、结构、委托、接口和枚举等)的成员和成员的信息。有了反射,即可对每一个类型了如指掌。另外我还可以直接创建对象,即使这个对象的类型在编译时还不知道。

 

重点:

System.Type类:System.Type 类对于反射起着核心的作用。但它是一个抽象的基类,Type有与每种数据类型对应的派生类,我们使用这个派生类的对象的方法、字段、属性来查找有关该类型的所有信息。获取给定类型的Type引用有3种常用方式:

//2.1、使用C# typeof运算符,是支持强类型的, 前提是项目必须引用反射类所在的程序集
Type personType1 = typeof(Person);
//2.2 使用对象GetType方法
Person p1 = new Person();
Type personType2 = p1.GetType();
//2.3调用Type类的静态方法GetType(),Type.GetType()是非强类型,如果参数typeName表示的目标类型不在当前程序集中,那么会返回nul
Type personType3 = Type.GetType("ReflectionStudy.Model.Person");
//此时会返回null,正确的做法是加载程序集,通过2.4的方式去获取

//2.4通过程序集类的Assembly的静态方法GetType()
Assembly assembly2 = Assembly.Load("ReflectionStudy.Model");
Type personType4 = assembly2.GetType("ReflectionStudy.Model.Person");

Type的属性及含义:

 

 

 Type的方法:

二、实际应用

创建一个实体类库ReflectionStudy.Model,创建两个实体类

namespace ReflectionStudy.Model
{
    //公司
    internal class Company
    {
        public string Name { get; set; }
        public string Description { get; set; }
        public string Owner { get; set; }    
    }
   //人员
    public class Person
    {
        public string Name  { get; set; }
        public int Age { get; set; }
        public int Id { get; set; }
        //不带set\get的为字段,带set/get的为属性
        public string Introduction;

        //公开方法
        public void IntroduceMyself(string word)
        {
            Console.WriteLine(word);
        }
        //私有方法
        private void TakeShower()
        {
            Console.WriteLine("洗澡");
        }

          //带参数的公共方法
          public void WriteString(string s, int i)
          {
               Console.WriteLine("WriteString:" + s + i.ToString());
          }
         //带一个参数的静态方法
         public static void StaticWriteString(string s)
         {
            Console.WriteLine("StaticWriteString:" + s);
         }

    }
}

创建一个控制台程序ReflectionStudy,引用程序集,在program.cs当中创建对应的方法

1、读取程序集

public static void  GetProgramAssembly()
        {
            //读取已引用的程序集
            // 引用的程序会编译后,生成dll文件存放在bin目录
            //D:\repos2022\ReflectionStudy\bin\Debug\net5.0\ReflectionStudy.Model.dll
            //获取ReflectionStudy.Model.dll中的实体类
            //三种方式等价:
            Assembly assembly1 = Assembly.LoadFrom("ReflectionStudy.Model.dll");
            Assembly assembly2 = Assembly.Load("ReflectionStudy.Model");
            Assembly assembly3 = Assembly.LoadFile(@"D:\repos2022\ReflectionStudy\bin\Debug\net5.0\ReflectionStudy.Model.dll");
            //遍历程序集中的所有class
            foreach (var type in assembly3.GetTypes())
           {
               Console.WriteLine($"名称:{type.Name},属性:{type.BaseType.Name}");
           }

        }

运行结果:

//1、读取程序集
{
                GetProgramAssembly();
                //结果打印:
                //名称:Person,属性:Object
                //名称:Company,属性:Object
}

2、根据类型创建实体类, Activator.CreateInstance(Type,。。。),第一个参数为需要创建对象的类型,后面的为调用构造函数的参数,下面例子为调用无参构造函数。

public static void GetEntityInfo()
        {
            //1、加载程序集
            Assembly assembly = Assembly.Load("ReflectionStudy.Model");
            //2、获取类型,注意带上完整的命名空间,三种方式
            Type personType = assembly.GetType("ReflectionStudy.Model.Person");

            //3、根据类型创建实体类
            object? person = Activator.CreateInstance(personType);
            //4.1、遍历属性
            foreach (var property in personType.GetProperties())
            {
                Console.WriteLine($"类:{personType.Name},属性:{property.Name}");
            }
            //4.2、遍历类的方法
            foreach (var method in personType.GetMethods())
            {
                Console.WriteLine($"类:{personType.Name},方法:{method.Name}");
            }
            //4.3遍历字段
            foreach (var field in personType.GetFields())
            {
                Console.WriteLine($"类:{personType.Name},字段:{field.Name}");
            }

        }

运行结果:

//2、利用反射获取类的相关信息(字段、属性、方法)
            {
                GetEntityInfo();
                //结果打印
                //类:Person,属性:Name
                //类:Person,属性:Age
                //类:Person,属性:Id
                //类:Person,方法:get_Name
                //类:Person,方法:set_Name
                //类:Person,方法:get_Age
                //类:Person,方法:set_Age
                //类:Person,方法:get_Id
                //类:Person,方法:set_Id
                //类:Person,方法:IntroduceMyself
                //类:Person,方法:GetType
                //类:Person,方法:ToString
                //类:Person,方法:Equals
                //类:Person,方法:GetHashCode
                //类:Person,字段:Introduction
            }

3、利用反射给类里的字段赋值、执行类里的方法

//设置、获取属性;执行方法
        public static void GetOrSetProperties()
        {
            //1、加载程序集
            Assembly assembly = Assembly.Load("ReflectionStudy.Model");
            //2、获取类型,注意带上完整的命名空间,三种方式
            Type personType = assembly.GetType("ReflectionStudy.Model.Person");
            //3、根据类型创建实体类
            object? person = Activator.CreateInstance(personType);
            //3、获取属性
            PropertyInfo ageInfo = personType.GetProperty("Age");
            PropertyInfo nameInfo = personType.GetProperty("Name");
            //4、属性赋值
            nameInfo.SetValue(person,"张三");
            ageInfo.SetValue(person,22);

            //打印
            Console.WriteLine($"姓名:{nameInfo.GetValue(person)},年龄:{ageInfo.GetValue(person)}");

            //5、获取方法
            ////BindingFlags类型枚举,BindingFlags.NonPublic | BindingFlags.Instance 组合才能获取到private私有方法,不带此参数默认为public的方法
            MethodInfo m1 = personType.GetMethod("TakeShower", BindingFlags.NonPublic | BindingFlags.Instance);
            MethodInfo m2 = personType.GetMethod("StaticWriteString");
            MethodInfo m3 = personType.GetMethod("WriteString");
            
            //6、执行方法
            // public object? Invoke(object? obj, object?[]? parameters);
            //6.1不带参
            m1.Invoke(person,null);
            //6.2 带一个个参
            m2.Invoke(person, new string[]{"哈哈哈"});
            //6.3 带多个参
            string test = "爱在";
            int i = 520;
            Object[] parametors = new Object[] {test, i};
            m3.Invoke(person, parametors);
        }

执行结果:

//3、设置或获取属性的值、执行方法
            {
                GetOrSetProperties();
                //执行结果:
                //姓名:张三,年龄:22
                //洗澡
                //StaticWriteString:哈哈哈
                //WriteString:爱在520
            }

4、可利用反射+简单工厂+配置文件实现不同数据库的灵活切换和配置,见附件代码

博客后台 - 博客园 (cnblogs.com)

 



这篇关于C# 反射 Reflection的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程