深圳网站设计 制作,环保网站主题,移动网站转换,淘宝网站的建设目标文章目录 前言一、前置知识1、编译器2、程序集#xff08;Assembly#xff09;3、元数据#xff08;Metadata#xff09; 二、反射1、反射的概念2、反射的作用3、反射的核心Type 类3.1 Type 类介绍3.2 不同方法获取 Type3.3 获取type类型所在的程序集的相关信息 4、反射的常… 文章目录 前言一、前置知识1、编译器2、程序集Assembly3、元数据Metadata 二、反射1、反射的概念2、反射的作用3、反射的核心Type 类3.1 Type 类介绍3.2 不同方法获取 Type3.3 获取type类型所在的程序集的相关信息 4、反射的常见用法4.1 获取 Type类型信息4.2 获取类的公共成员4.3 获取类的构造函数4.4 获取类的字段4.5 获取类的成员方法4.6 其他反射操作 5、反射访问并操作私有private成员包括字段、属性、方法等6、总结 三、关键类Assembly和Activator1、**反射中的关键类**1.1 Type 类前面已经介绍过了1.2 Assembly 类1.3 Activator 类 2、**使用 Activator 类实例化对象**示例 1无参构造函数示例 2带参数的构造函数 3、**使用 Assembly 类加载程序集**3.1 加载程序集1获取当前执行的程序集2从指定路径加载程序集 3.2 获取指定名称的类型3.3 获取所有类型3.4 获取并调用类型中的方法 4、综合示例 四、**总结**专栏推荐完结 前言
C# 反射 (Reflection) 是一种强大的机制它允许程序在运行时动态地查看和操作程序的元数据。通过反射我们可以在不事先知道类型信息的情况下获取类型信息并操作类型中的成员如类、方法、属性、字段等。
一、前置知识
1、编译器
编译器是将源语言程序如 C#、Java、C 等翻译为目标语言程序如机器代码或伪机器代码的工具。源代码经过编译器的处理后可以生成可执行文件 (.exe) 或库文件 (.dll) 供程序运行时使用。
2、程序集Assembly
程序集是经过编译器编译得到的中间产物可以是一个库文件 (.dll) 或可执行文件 (.exe)。它是 .NET 程序中的代码集合包含了类型、方法、属性等元数据是程序执行的基础。
程序集就是我们写的一个代码集合我们现在写的所有代码最终都会被编译器翻译为一个程序集供别人使用。比如一个代码库文件(dll)或者一个可执行文件(exe)
我们新建一个C#项目运行一次后系统就会自动在bin目录里生成对应的程序集文件
3、元数据Metadata
在C#中元数据Metadata指的是描述程序及其组成部分如类、方法、属性、字段等的信息。它是关于代码的“数据”但并不是实际执行的代码。可以将元数据视为程序代码的“数据字典”它提供了有关类型、成员和引用等的信息使得程序在运行时可以进行动态操作。
元数据是随代码一起编译成程序集Assembly的一部分并且可以在运行时通过反射机制进行访问。
元数据是指有关程序结构和组成部分的描述信息它不仅有助于程序的运行时类型检查还为反射、动态加载等功能提供了支持。在C#中元数据通常存储在程序集文件中并可以通过反射机制访问和操作。
二、反射
1、反射的概念
反射允许程序在运行时查看其他程序集或自身的元数据动态获取类型信息并对其进行操作。例如通过反射可以查看类的构造函数、方法、字段、属性等甚至可以在运行时动态创建对象并调用方法。
2、反射的作用
程序运行时获取类型信息反射可以帮助我们动态获取类、方法、字段等的元数据信息。动态操作对象反射使得我们可以动态实例化对象调用方法修改字段值等。增强灵活性反射使得代码更加灵活尤其在插件机制、依赖注入等场景下尤为重要。
3、反射的核心Type 类
3.1 Type 类介绍
Type 类是反射的基础它提供了关于类的所有信息例如类的名称、构造函数、方法、字段、属性等。它是访问元数据的主要方式。
3.2 不同方法获取 Type 通过对象的 GetType() 方法获取 int a 42;
Type type a.GetType();
Console.WriteLine(type); // 输出: System.Int32通过 typeof 关键字获取 Type type typeof(int);
Console.WriteLine(type); // 输出: System.Int32通过类的全名获取 Type type Type.GetType(System.Int32);
Console.WriteLine(type); // 输出: System.Int32其实就是命名空间.类名
3.3 获取type类型所在的程序集的相关信息
Console.WriteLine(type.Assembly);打印
System.Private.CoreLib, Version9.0.0.0, Cultureneutral, PublicKeyToken7cec85d7bea7798eSystem.Private.CoreLib 是程序集的名称。通常是 .exe 或 .dll 文件的名称Version9.0.0.0 是程序集的版本。Cultureneutral 表示它是通用区域设置的。如果是通用语言程序集则为 neutral。PublicKeyToken7cec85d7bea7798e 是公共密钥标识符。用于区分同名的不同程序集版本。
4、反射的常见用法
为了方便测试反射的常见用法这里我先新增一个简单的类里面包含基本的构造函数、方法、字段、属性。
ps:这里只是测试探究实际开发肯定不需要通过反射来操作自己的类。
class Test
{private int i 1;public int j 2;public int k { get; set; }public string str 向宇的客栈;public Test() { }public Test(int i){this.i i;}public Test(int i, string str) : this(i){this.str str;}public void Log(){Console.WriteLine(学习C#);}
}4.1 获取 Type类型信息
Type t typeof(Test);// 获取类型信息4.2 获取类的公共成员
使用 GetMembers() 方法获取类中的所有公共成员
MemberInfo[] infos t.GetMembers();
foreach (var info in infos)
{Console.WriteLine(info);
}打印结果
可以看到除了万物之父object中的方法打印出来的都是我们前面Test类中定义的公共成员信息构造函数、方法、字段、属性。
4.3 获取类的构造函数
获取所有构造函数
ConstructorInfo[] ctors t.GetConstructors();
foreach (var ctor in ctors)
{Console.WriteLine(ctor);
}结果
获取无参构造函数并调用
ConstructorInfo ctor t.GetConstructor(Type.EmptyTypes);
Test obj (Test)ctor.Invoke(null);
Console.WriteLine(obj);获取有参构造函数并调用
ConstructorInfo ctor t.GetConstructor(new Type[] { typeof(int), typeof(string) });
Test obj (Test)ctor.Invoke(new object[] { 10, Hello });
Console.WriteLine(obj);4.4 获取类的字段
获取所有公共字段
FieldInfo[] fieldInfos t.GetFields();
foreach (var field in fieldInfos)
{Console.WriteLine(field);
}结果
获取指定字段
FieldInfo fieldInfo t.GetField(j);
Console.WriteLine(fieldInfo);结果
使用反射获取和设置字段的值
FieldInfo fieldInfo t.GetField(j);
Test test new Test();
test.j 99;
fieldInfo.SetValue(test, 100);
Console.WriteLine(fieldInfo.GetValue(test)); // 输出: 1004.5 获取类的成员方法
获取所有公共方法记得引入using System.Reflection;反射命名空间
using System.Reflection;MethodInfo[] methods t.GetMethods();
foreach (var method in methods)
{Console.WriteLine(method);
}获取指定方法并调用
MethodInfo method t.GetMethod(ToString);
object result method.Invoke(test, null);
Console.WriteLine(result);对于方法重载需要提供方法的参数类型
MethodInfo method t.GetMethod(Substring, new Type[] { typeof(int), typeof(int) });
object result method.Invoke(Hello, World!, new object[] { 7, 5 });
Console.WriteLine(result); // 输出: World4.6 其他反射操作 枚举 获取枚举类型的名称 Enum.GetEnumName(typeof(DayOfWeek), 1); // 输出: Monday事件 获取类的事件 EventInfo eventInfo t.GetEvent(MyEvent);
Console.WriteLine(eventInfo);接口 获取类实现的接口 Type[] interfaces t.GetInterfaces();
foreach (var iface in interfaces)
{Console.WriteLine(iface);
}属性 获取类的属性 PropertyInfo[] properties t.GetProperties();
foreach (var property in properties)
{Console.WriteLine(property);
}5、反射访问并操作私有private成员包括字段、属性、方法等
在默认情况下反射是可以访问私有成员的但你必须明确告诉 .NET 你要访问它们。具体来说你需要使用 BindingFlags 来指定访问私有成员。
例如默认情况下反射会忽略私有字段和方法但你可以通过显式地指定 BindingFlags.NonPublic 和 BindingFlags.Instance 来让反射能够访问这些私有成员。
示例
// 创建 MyClass 的实例
MyClass myObject new MyClass();// 获取 MyClass 类型的信息
Type type typeof(MyClass);// 使用反射访问私有字段 secretValue
FieldInfo fieldInfo type.GetField(secretValue, BindingFlags.NonPublic | BindingFlags.Instance);
if (fieldInfo ! null)
{// 获取字段的值var value fieldInfo.GetValue(myObject);Console.WriteLine(Private Field secretValue Value: value);
}// 使用反射访问私有方法 PrintSecret
MethodInfo methodInfo type.GetMethod(PrintSecret, BindingFlags.NonPublic | BindingFlags.Instance);
if (methodInfo ! null)
{// 调用私有方法methodInfo.Invoke(myObject, null);
}这种机制虽然强大但应谨慎使用避免破坏封装性尤其是在生产环境中。这种做法通常用于调试、测试、动态代理等特殊场景。
6、总结
C# 中的反射为程序提供了强大的动态能力通过 Type 类及其提供的方法我们可以在运行时获取类型的元数据动态创建对象调用方法甚至修改字段值。
虽然反射提供了灵活性但是反射的性能较差因为它是动态查找和操作的。如果频繁使用反射可能会影响程序性能尤其是在处理大量数据时。因此应该在合适的场合使用反射并考虑到性能和可维护性。
三、关键类Assembly和Activator
反射Reflection是 C# 中一个非常强大的机制它允许在运行时访问和操作程序中的类型信息。通过反射程序可以在运行时动态地获取类、方法、属性等信息并实例化对象、调用方法、访问字段等。下面是对 Assembly 和 Activator 这两个关键类的详细介绍和相关用法。
1、反射中的关键类
1.1 Type 类前面已经介绍过了
Type 类提供了对类型信息的访问它是所有类型的元数据的基础。通过 Type 对象你可以获取类的构造函数、属性、字段、方法等信息。
1.2 Assembly 类
Assembly 类用于加载和操作程序集。程序集包含了类、接口、枚举等信息反射可以用来加载程序集并且在程序运行时查找类型和操作它们。
1.3 Activator 类
Activator 类位于 System 命名空间下提供了一组静态方法来创建类型的实例。它常用于在运行时通过反射动态创建对象特别是在我们事先不知道具体类型的情况下。例如依赖注入、插件加载、对象池等场景中可以使用 Activator 来动态实例化对象。
2、使用 Activator 类实例化对象
Activator 提供了几种创建对象的方法最常用的是 CreateInstance它可以用来根据 Type 对象动态创建实例。
示例 1无参构造函数
假设有一个类 Test它有一个无参构造函数。你可以使用 Activator.CreateInstance 来创建该类的实例。
using System;public class Test
{public string str Hello, World!;
}class Program
{static void Main(){// 获取 Test 类的 Type 对象Type testType typeof(Test);// 使用 Activator 创建对象实例Test testobj Activator.CreateInstance(testType) as Test;// 输出结果Console.WriteLine(testobj.str); // Output: Hello, World!}
}示例 2带参数的构造函数
如果类有带参数的构造函数可以使用 Activator.CreateInstance 方法传递参数来创建实例。
using System;public class Test
{public int j;public string str;// 构造函数public Test(int j, string str){this.j j;this.str str;}
}class Program
{static void Main(){Type testType typeof(Test);// 使用带参数的构造函数Test testobj1 Activator.CreateInstance(testType, 99, Hello) as Test;Console.WriteLine(testobj1.j); // Output: 99Console.WriteLine(testobj1.str); // Output: HelloTest testobj2 Activator.CreateInstance(testType, 55, World) as Test;Console.WriteLine(testobj2.j); // Output: 55Console.WriteLine(testobj2.str); // Output: World}
}3、使用 Assembly 类加载程序集
前面我们一直都是对当前程序的操作实际开发肯定是操作外部程序集。Assembly 类使得我们可以在运行时加载、查询、获取外部程序集的类型信息等。
常用方法
GetExecutingAssembly()获取当前执行的程序集。LoadFrom(string path)从指定路径加载程序集。GetTypes()获取程序集中的所有类型。GetType(string typeName)获取指定名称的类型。GetManifestResourceNames()获取程序集中的所有嵌入式资源名称。
3.1 加载程序集
1获取当前执行的程序集
Assembly assembly Assembly.GetExecutingAssembly();
Console.WriteLine(当前程序集: assembly.FullName);2从指定路径加载程序集
Assembly assembly Assembly.LoadFrom(path/to/your/assembly.dll);
Console.WriteLine(加载程序集: assembly.FullName);3.2 获取指定名称的类型
Assembly assembly Assembly.LoadFrom(path/to/your/assembly.dll);
Type type assembly.GetType(Namespace.ClassName); // 获取程序集中的某个类型
Console.WriteLine(类型名称: type.FullName);3.3 获取所有类型
Assembly assembly Assembly.LoadFrom(path/to/your/assembly.dll);
Type[] types assembly.GetTypes();
foreach (Type t in types)
{Console.WriteLine(t.FullName); // 输出程序集中的所有类型
}3.4 获取并调用类型中的方法
其实就是结合前面Type知识配合使用
Assembly assembly Assembly.LoadFrom(path/to/your/assembly.dll);
Type type assembly.GetType(Namespace.ClassName);
MethodInfo methodInfo type.GetMethod(MethodName);
object result methodInfo.Invoke(null, null); // 调用静态方法
Console.WriteLine(result);4、综合示例
我这里项目的程序集文件路径为C:\Users\zw\Desktop\C#\bin\Debug\net9.0\C#.dll 程序集文件里主要的代码是我自定义了一个Test类
代码
using System;
using System.Reflection;class Program
{static void Main(){// 加载程序集(因为是反斜杠所以加个不希望它转义)Assembly assembly Assembly.LoadFrom(C:\Users\zw\Desktop\C#\bin\Debug\net9.0\C#.dll);// 获取程序集中的所有类型Type[] types assembly.GetTypes();foreach (var type in types){Console.WriteLine(type.Name);}// 获取程序集中的一个具体类型Type testType assembly.GetType(Test);// 使用反射实例化对象object myClassObj Activator.CreateInstance(testType);// 获取并调用方法MethodInfo method testType.GetMethod(Log);method.Invoke(myClassObj, null);// 获取指定字段并打印值FieldInfo field1 testType.GetField(str);Console.WriteLine(field1.GetValue(myClassObj));// 获取指定字段先修改值在打印值FieldInfo field2 testType.GetField(j);field2.SetValue(myClassObj, 100);Console.WriteLine(field2.GetValue(myClassObj));}
}结果程序集里的方法被执行字段也被打印了Program类和E_Days枚举是定义的其他两个测试数据不用管即可
四、总结
Type提供关于类、接口、枚举等类型的信息。Assembly用于加载程序集可以获取程序集中的所有类型和类型成员信息。Activator用于在运行时动态创建对象实例支持无参数和带参数构造函数。反射虽然非常强大但会影响性能因此在需要时使用避免过度依赖。 专栏推荐
地址【从零开始入门unity游戏开发之——C#篇】【从零开始入门unity游戏开发之——unity篇】【制作100个Unity游戏】【推荐100个unity插件】【实现100个unity特效】【unity框架开发】
完结
赠人玫瑰手有余香如果文章内容对你有所帮助请不要吝啬你的点赞评论和关注你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法也欢迎评论私信告诉我哦
好了我是向宇https://xiangyu.blog.csdn.net
一位在小公司默默奋斗的开发者闲暇之余边学习边记录分享站在巨人的肩膀上通过学习前辈们的经验总是会给我很多帮助和启发如果你遇到任何问题也欢迎你评论私信或者加群找我 虽然有些问题我也不一定会但是我会查阅各方资料争取给出最好的建议希望可以帮助更多想学编程的人共勉~