当前位置: 首页 > article >正文

详解C#反射(Reflection)

一.反射概述

1.1 自己对反射掌握程度的要求

了解反射相关概念,API,阅读代码和文档时看到反射相关内容能反应过来即可

1.2 反射的概念

反射提供了封装程序集、模块和类型的对象(Type类型)。可以使用反射动态创建类型的实例,将类型绑定到现有对象,或从现有对象获取类型并调用其方法或访问其字段和属性。如果代码中使用了属性,可以利用反射对它们进行访问。

这里需要重点理解的一句话,反射可以动态的创建类型的实例。那么什么是静态创建类型的实例呢?通俗的说就是最常见的  类名 对象名 = new 类名() 的形式

要理解为什么要动态创建类型的实例,需要先了解程序集的概念

1.3 程序集概述

  •  程序集采用可执行文件 (.exe) 或动态链接库文件 (.dll) 的形式,是 .NET 应用程序的构建基块 
  • 在 .NET中,可从一个或多个源代码文件生成程序集,程序集可以包含一个或多个模块。
  • 程序集以 .exe 或 .dll 文件的形式实现 。
  • 程序集使用方式一:静态引用(见下图所示), 引用程序集后,应用程序可以使用其名称空间的所有可访问类型、属性、方法和其他成员,就好像它们的代码是源文件的一部分一样。

生成dll01.dll的代码如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace dll01
{
    public class Player01
    {
        private int id = 1011112222;

        public void Work()
        {
            Console.WriteLine("id:" + id);
        }

    }
}

可以看出,VS引用dll后,可以常规使用其Class

参考:.NET 中的程序集

1.4 动态加载程序集

下列示例演示动态加载并使用程序集:用代码在需要的位置加载程序集,在不知道程序集命名空间,类名,方法的情况下,创建对象,调用方法。

通过这个例子,可以知道在有常规  类名.new()  创建对象的情况下,为什么还有反射创建对象的需要。

namespace LearnCS1
{
    
    class MainClass
    {
        static void Main()
        {
            //step1.加载程序集
            Assembly a1 = Assembly.Load("dll01");
            //step2.获取所有类名
            Type[] allClass = a1.GetTypes();
            foreach (var oneClass in allClass)
            {
                //step3.根据类名创建对象
                object obj = a1.CreateInstance(oneClass.FullName);
                //step4.获取所有方法
                MethodInfo[] allMethodInfo = oneClass.GetMethods();
                foreach (var oneMethod in allMethodInfo)
                {
                    //step5.调用无参方法
                    if (oneMethod.ReturnParameter.ParameterType.Name == "Void")
                        oneMethod.Invoke(obj, null);
                }
            }
            Console.ReadKey();
        }
    }
}

1.5反射的用途

  • 需要访问程序元数据的属性。

  • 检查和实例化程序集中的类型。

  • 在运行时构建新类型。使用System.Reflection.Emit中的类。

  • 执行后期绑定,访问在运行时创建的类型的方法。

参考C#官方手册:

反射(C# 编程指南)

.NET 中的反射

二.反射核心类System.Type用法

2.1 获取Class的基础信息

namespace LearnCS1
{
    class CTest { }
    class MainClass
    {
        static void Main()
        {
            //获取Class的基础信息
            Type t1 = typeof(CTest);    //typeof的参数是类名
            Console.WriteLine("type.Name:" + t1.Name);
            Console.WriteLine("type.FullName:" + t1.FullName);
            Console.WriteLine("type.Namespace:" + t1.Namespace);
            Console.WriteLine("type.IsAbstract:" + t1.IsAbstract);
            Console.WriteLine("type.IsClass:" + t1.IsClass);
            Console.WriteLine("type.IsEnum:" + t1.IsEnum);
            Console.WriteLine("type.IsInterface:" + t1.IsInterface);
            Console.WriteLine("type.IsSealed:" + t1.IsSealed);
            Console.WriteLine("type.IsValueType:" + t1.IsValueType);
            Console.WriteLine("type.IsGenericType:" + t1.IsGenericType);
            Console.WriteLine("type.AssemblyQualifiedName:" + t1.AssemblyQualifiedName);
        }

    }
}

2.2 获取public字段

测试类如下

namespace ns_UserInfo
{
    public class UserInfo
    {
        private int _num = 0;

        public string Phone = "15911112222";

        public string Name { get; set; }

        public string Address { get; set; }

        public UserInfo()
        {

        }

        public UserInfo(string name)
        {

        }

        public int PublicMethod()
        {
            return int.MinValue;
        }

        internal void InternalMethod()
        {

        }
        private void PrivateMethod()
        {

        }
    }
}
using System;
using System.Reflection;    //引入反射命名空间
using ns_UserInfo;

namespace LearnCS1
{
    
    class MainClass
    {
        static void Main()
        {

            UserInfo userInfo = new UserInfo();
            Type type = userInfo.GetType();
            //获取Public字段
            FieldInfo[] fieldInfos = type.GetFields();
            foreach (var item in fieldInfos)
            {
                Console.WriteLine("字段:" + item.Name + "  类型:"+item.FieldType);
            }
        }
    }
}

2.3 获取public属性

namespace LearnCS1
{
    
    class MainClass
    {
        static void Main()
        {

            UserInfo userInfo = new UserInfo();
            Type type = userInfo.GetType();
            //获取Public属性
            PropertyInfo[] propertyInfos = type.GetProperties();
            foreach (var item in propertyInfos)
            {
                Console.WriteLine("属性:" + item.Name + "  类型:"+item.PropertyType);
            }
        }
    }
}

2.4 获取public方法

namespace LearnCS1
{
    
    class MainClass
    {
        static void Main()
        {

            UserInfo userInfo = new UserInfo();
            Type type = userInfo.GetType();
            //获取Public方法
            MethodInfo[] methodInfos = type.GetMethods();
            foreach (var item in methodInfos)
            {
                Console.WriteLine("方法:" + item.Name + "  类型:"+item.ReturnType);
            }
        }
    }
}

2.5 获取public构造函数

namespace LearnCS1
{
    
    class MainClass
    {
        static void Main()
        {

            UserInfo userInfo = new UserInfo();
            Type type = userInfo.GetType();
            ConstructorInfo[] constructorInfos = type.GetConstructors();//GetConstructors()获取所有的公共的构造函数
            for (int i = 1; i <= constructorInfos.Length; i++)
            {
                ParameterInfo[] parameterInfos = constructorInfos[i-1].GetParameters();//GetParameters()获取指定方法或构造函数的参数
                Console.WriteLine("构造函数" + i + "," + "参数数量:" + parameterInfos.Length);
                foreach (var param in parameterInfos)
                {
                    Console.WriteLine("参数名:" + param.Name + ",返回类型:" + param.ParameterType);
                }

            }
        }
    }
}

2.6 获取public成员(包含以上所有)

namespace LearnCS1
{
    
    class MainClass
    {
        static void Main()
        {

            UserInfo userInfo = new UserInfo();
            Type type = userInfo.GetType();
            MemberInfo[] memberInfos = type.GetMembers();
            foreach (var item in memberInfos)
            {
                Console.WriteLine("成员名称:" + item.Name + ",成员类型:" + item.MemberType);
            }
        }
    }
}

2.6 通过枚举BindingFlags进行过滤

namespace LearnCS1
{
    
    class MainClass
    {
        static void Main()
        {

            UserInfo userInfo = new UserInfo();
            Type type = userInfo.GetType();
            MemberInfo[] memberInfos = type.GetMembers(BindingFlags.NonPublic | BindingFlags.Instance);
            foreach (var item in memberInfos)
            {
                Console.WriteLine("成员名称:" + item.Name + ",成员类型:" + item.MemberType);
            }
        }
    }
}

示例说明

  • GetMembers 中传入 BindingFlags 相当于是对成员信息进行一个过滤
  • 除了GetMembers ,很多方法都可以传BindingFlags进行过滤
  • BindingFlags 是位标志枚举,可使用 | & ^ 等运算符 | 表示取并集,& 表示取交集,^ 表示取差集
  • BindingFlags.XXX | BindingFlags.Instance是固定写法,从提示信息上可以看出

2.7 获取Type 对象的三个方法

namespace LearnCS1
{
    
    class MainClass
    {
        static void Main()
        {
            //1.typeof(类名)
            Type t1 = typeof(UserInfo);
            Console.WriteLine("t1.FullName:" + t1.FullName);
            //2.对象.GetType()
            UserInfo userInfo = new UserInfo();
            Type t2 = userInfo.GetType();
            Console.WriteLine("t2.FullName:" + t2.FullName);
            //3.Type.GetType("命名空间.类名");
            Type t3 = Type.GetType("ns_UserInfo.UserInfo");
            Console.WriteLine("t3.FullName:" + t3.FullName);
        }
    }
}

三.加载程序集的API

namespace LearnCS1
{
    class MainClass
    {
        static void Main()
        {
            //加载程序集
            //方式1:将dll01.dll放在DEBUG目录下,传入无后缀名称即可加载
            Assembly a1 = Assembly.Load("dll01");
            //方式2:传入文件路径,会加载dll01.dll依赖的程序集
            Assembly a2 = Assembly.LoadFrom(AppDomain.CurrentDomain.BaseDirectory + "dll01.dll");
            //方式3:传入文件路径,不会会加载dll01.dll依赖的程序集
            Assembly a3 = Assembly.LoadFile(AppDomain.CurrentDomain.BaseDirectory + "dll01.dll");
            Console.WriteLine(a1.FullName);
            Console.WriteLine(a2.FullName);
            Console.WriteLine(a3.FullName);
            
        }
    }
}

三.反射创建对象API

  • 方式一:通过Assembly.CreateInstance
  • 方式二:Activator.CreateInstance
  • 方式三:通过调用构造函数
namespace LearnCS1
{
    
    class MainClass
    {
        static void Main()
        {
            //step1.加载程序集
            Assembly a1 = Assembly.Load("dll01");
            //step2.获取所有类名
            Type[] allClass = a1.GetTypes();
            foreach (var oneClass in allClass)
            {
                //step3.根据类名创建对象
                //方式一:通过Assembly.CreateInstance
                object obj1 = a1.CreateInstance(oneClass.FullName);
                //方式二:Activator.CreateInstance
                object obj2 = Activator.CreateInstance(oneClass);
                //方式三:通过调用构造函数
                ConstructorInfo conInfo = oneClass.GetConstructor(new Type[] { });
                object obj3 = conInfo.Invoke(new object[] { });
                //step4.获取所有方法
                MethodInfo[] allMethodInfo = oneClass.GetMethods();
                foreach (var oneMethod in allMethodInfo)
                {
                    //step5.调用无参方法
                    if (oneMethod.ReturnParameter.ParameterType.Name == "Void")
                    {
                        oneMethod.Invoke(obj1, null);
                        oneMethod.Invoke(obj2, null);
                        oneMethod.Invoke(obj3, null);
                    }
                        
                }
            }
            Console.ReadKey();
        }
    }
}


http://www.kler.cn/a/509411.html

相关文章:

  • 在21世纪的我用C语言探寻世界本质——字符函数和字符串函数(2)
  • 递归40题!再见递归
  • AI-Talk开发板之替换唤醒词
  • 粒子群优化 (PSO, Particle Swarm Optimization) 算法详解及案例分析
  • AWS S3 跨账户访问 Cross Account Access
  • ZooKeeper 核心知识全解析:架构、角色、节点与应用
  • JAVA:解释器模式(Interpreter Pattern)的技术指南
  • PCM5142集成32位384kHz PCM音频立体声114dB差分输出DAC编解码芯片
  • 第三节 从善如流
  • 2025年供应链攻击或成企业主要威胁
  • Leiden算法一种用于社区检测的图聚类算法
  • Swift 趣味开发:查找拼音首字母全部相同的 4 字成语(下)
  • 题解 CodeForces 430B Balls Game 栈 C/C++
  • MySQL HASH索引详解
  • 从 Web1 到 Web3:互联网发展的历史与未来
  • ESP32学习笔记_FreeRTOS(6)——Event and Notification
  • openharmont驱动子系统
  • Wi-Fi 7、Wi-Fi 6 与 5G、4G 的全方位对比
  • ES语法学习2
  • 华为昇腾910B1基于 LoRA 的 Qwen2.5-7B-Instruct 模型微调
  • 通过ffmpeg将FLV文件转换为MP4
  • DPIN与CESS Network达成全球战略合作,推动DePIN与AI领域创新突破
  • Redis可视化工具--RedisDesktopManager的安装
  • 考前64天 学习笔记 - 形成“习惯体系”进行最小启动
  • Docker(C/S架构软件)的介绍与使用、安装详解
  • mybatis学习(7/134)