#本文概括
类型 | 执行目标 | 目标举例 | 描述 | 使用 |
Type | 类 | public class A | 包含类型的特性 | |
Assembly | 程序集 | ClassLibrary.dll | 表示一个程序集 | |
FieldInfo | 类中字段 | public string name; | 发现字段的属性并提供对字段元数据的访问权限 | |
MemberInfo | 成员 | 所有成员 | 获取有关成员属性的信息并提供对成员元数据的访问权限 |
![]() |
MethodInfo | 方法 | public string AddValue(string prefix)//自定义函数 { return value + "+" + prefix; } | 发现方法的属性并提供对方法元数据的访问 | |
Activator | Type | Type t = Type.GetType("Test.TestC"); | 实例化类型Type |
一、命名空间:System.Reflection
#概述
在C#中,我们要使用反射,首先要搞清楚以下命名空间中几个类的关系:
System.Reflection命名空间
(1) AppDomain:应用程序域,可以将其理解为一组程序集的逻辑容器
(2) Assembly:程序集类
(3) Module:模块类
(4) Type:使用反射得到类型信息的最核心的类
他们之间是一种从属关系,也就是说,一个AppDomain可以包含n个Assembly,一个Assembly可以包含n个Module,而一个Module可以包含n个Type。
*Type类型
1)概述
命名空间:System (Type类在更多地方使用,而不仅仅是System.Reflection)
程序集:netstandard.dll
官方文档:链接地址
Type类,用来包含类型的特性。对于程序中的每一个类型,都会有一个包含这个类型的信息的Type类的对象,类型信息包含数据,属性和方法等信息。
Type是很多反射功能的入口,它实现很多方法和属性,可用的属性都是只读的:可以使用Type确定数据的类型,但不能使用它修改该类型。
2)通过反射获取类型(生成Type类的对象)
通过反射获取类型 | 【通过类名获取类别】 System.Type t = typeof(PersonNameSpace.Person) //举例:Type t2 = typeof(double); | ||
【通过对象,获取类别】 PersonNameSpace.Person p = new PersonNameSpace.Person(); System.Type t = p.GetType(); //举例:int i = 41; Type t1 = i.GetType(); | 注意:该情况下,返回的Type对象只与该数据类型相关,不包含该类型实例的任何信息 | ||
【通过类名的字符串,获取类别】 System.Type t2 = System.Type.GetType("System.String"); //举例:Type t3 = Type.GetType("System.Double");
| 参数: 情况一:是正在执行的程序集或mscorlib.dll/System.Private.CoreLib.dl装配件中声明的类型皆可以省略装配件名称(.Net装配件编译的时候,默认都引用了mscorlib.dll,除非在编译的时候明确指定不引用它) ->命名空间限定的类型名称,即”命名空间.类名“ 情况二:其他 ->要获取的类型的程序集限定名称 |
System.Double是在mscorlib.dll中声明的,故Type t3 = Type.GetType(“System.Double”)是正确的 System.Data.DataTable是在System.Data.dll中声明的,那么:Type.GetType(“System.Data.DataTable”)就只能得到空引用, |
若这里仍不清楚,可跳至最后了解代码实际操作进行理解。
3)属性
返回类型 | 方法名称 | 描述 |
String | Name | 数据类型名 |
String? | FullName | 数据类型的完全名(包含名称空间)【全名】 |
String? | Namespace | 定义数据类型的名称空间名 |
Type? | BaseType | 父类类型 |
Type | UnderlyingSystemType | 该Type在.NET运行库中映射到的类型 |
bool | IsAbstract,IsArray,IsClass,IsEnum等 | 判断Type是什么类型的属性 |
类型后加"?",表示可为空的类型。
4)方法
操作目标 | 返回值 | 方法名称 | 描述 |
成员 | MemberInfo? | 搜索具有指定名称的公共成员。 | |
MemberInfo[] | 返回为当前 Type 的所有公共成员(不包含构造函数) | ||
方法 | MethodrInfo | 搜索具有指定名称的公共方法。 | |
MethodrInfo[] | 返回为当前 Type 的所有公共方法 | ||
字段 | FieldInfo? | 用于取得该类的字段(成员变量)的信息 | |
FieldInfo[] | 访问所有字段包括静态和非静态 |
5)实验
代码 1
展示Type部分属性
namespace ConsoleApp1
{
public class Program
{
static void Main(String[] args)
{
Console.WriteLine("\t\t\t数据类型名\t数据类型的完全限定名(包含名称空间)\t"
+"在其中定义数据类型的名称空间名\t该对象的直接基本类型\t该Type在.NET运行库中映射到的类型");
//对象调用GetType()函数:
int i = 41;
Type t1 = i.GetType();
Console.WriteLine(t1+"\t\t" +
t1.Name+ "\t\t\t" +
t1.FullName + "\t\t\t\t" +
t1.Namespace+ "\t\t\t" +
t1.BaseType + "\t" +
t1.UnderlyingSystemType);
//使用typeof运算符,这个运算符的参数是类型的名称,但不放在引号中:
Type t2 = typeof(A);
Console.WriteLine(t2+"\t" +
t2.Name + "\t\t\t" +
t2.FullName + "\t\t\t" +
t2.Namespace + "\t\t" +
t2.BaseType + "\t\t" +
t2.UnderlyingSystemType);
/* Console.WriteLine(t.id); */ //报错,不可使用A类型实例的任何信息
//调用Type类的静态方法GetType():
Type t3 = Type.GetType("System.Double");
Console.WriteLine(t3+"\t\t" +
t3.Name + "\t\t\t" +
t3.FullName + "\t\t\t\t" +
t3.Namespace + "\t\t\t" +
t3.BaseType + "\t" +
t3.UnderlyingSystemType);
}
//自定义类
public class A
{
public int id { get; set; }
}
}
}
结果
![](https://i-blog.csdnimg.cn/blog_migrate/dc9fd300a093d1230c34bf4ae772ca6a.png)
代码2
实验方法
using System.Reflection;
namespace ConsoleApp1
{
public class Program
{
static void Main(String[] args)
{
//1.创建Type对象,类为同程序集下Test命名空间里的TestC
Type t = Type.GetType("Test.TestC");
//2.获取Type对象的所有公共成员
MemberInfo[] ms = t.GetMembers();
//循环打印所有公共成员
foreach (var m in ms)
{
Console.WriteLine(m);
}
}
}
}
//另一个命名空间
namespace Test
{
//一个简单的类,包含一个无参的构造器,一个有参数的构造器,一个GetValue的方法,一个value属性
public class TestC
{
//自定义字段
public string value { get; set; }
//构造函数
public TestC()
{ Console.WriteLine("无参的构造函数"); }
public TestC(string v)
{
value = v;
}
//自定义函数
public string GetValue(string prefix)
{
return "NULL";
}
private string GetVs()
{
return "Vs";
}
}
}
结果
![](https://i-blog.csdnimg.cn/blog_migrate/f657f655a3c05e6085481588c53177cf.png)
1.Assembly 程序集
1)概述
官方文档:链接地址
Assembly =装配件 = 程序集。表示一个程序集,它是一个可重用、无版本冲突并且可自我描述的公共语言运行时应用程序构建基块。(编译出来的.dll、.exe)
命名空间:System.Reflection
程序集:System.Runtime.dll
使用 Assembly 类加载程序集、浏览程序集的元数据和构成部分、发现程序集中包含的类型以及创建这些类型的实例。
程序集主要分为两种类型:强命名程序集、弱命名程序集。这两种程序集的结构上完全一致的。两者真正的区别在于强命名程序集用发布者的公钥/私钥允许对程序集的惟一标识。
1、强命名程序集
一个强命名程序集包括4个重要属性:一个文件名(无扩展名)、一个版本号、一个语言文化标识以有一个公钥。這些属性惟一地标识了程序集。
2、弱命名程序集
简单地说,没有以上强命名程序集所有的属性就是弱命名程序集.
2)动态加载程序集
Load、LoadFrom和LoadWithPartialName
动态加载程序集 (using System.Reflection;) | 【利用Load方法】 Assembly a = Assembly.Load("Y_TestProject"); //错误举例“Assembly.Load("Y_TestProject.dll");"报错 ![]() | 注意:
| 参数: 强程序集:提供程序集的详细信息,例“MyAssembly,Version=1.0.0.0,culture=zhCN,PublicKeyToken=47887f89771bc57f” 弱程序集:程序集的名称【不包含扩展名】 |
【利用LoadForm方法】 Assembly a = Assembly.LoadFrom("Y_TestProject.dll"); //也可以Assembly a = Assembly.LoadFrom("Y_TestProject"); | 注意:
| 参数:指定一个程序集文件的路径名【扩展名or不包含】 | |
...... | ... |
若这里仍不清楚,可跳至最后了解代码实际操作进行理解。
3)方法
返回值 | 方法名称 | 描述 |
注意:详情请看上面"通过反射获取类型(生成Type类的对象)" |
4)实验
代码
框架
![](https://i-blog.csdnimg.cn/blog_migrate/1477da1405caec987d26221fe3e02d4e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/dedba6c896003952dd08dcb0d9b9a22d.png)
主项目——控制台
using System.Reflection;
namespace Y_TestProject
{
public class Program
{
static void Main(string[] args)
{
/*--获取本项目的程序集,获取其下指定类,调用指定方法--*/
Assembly a = Assembly.LoadFrom("Y_TestProject.dll");
//“Assembly.Load("Y_TestProject.dll");"报错,系统找不到指定的文件
//Type t0 = a.GetType("AC"); //t为null
Type t1 = a.GetType("Y_TestProject.AC"); //正确
MethodInfo mi1 = t1.GetMethod("ACP");
mi1.Invoke(Activator.CreateInstance(t1), null);
/*--获取导入的dll文件,获取其下指定类,调用指定方法--*/
//Assembly aClassLibrary0 = Assembly.Load("ClassLibrary.dll");//报错,系统找不到指定的文件
Assembly aClassLibrary1 = Assembly.LoadFrom("ClassLibrary.dll"); //正确
Type t3 = aClassLibrary1.GetType("LeiKu.Pub"); //获取类型
MethodInfo mi2= t3.GetMethod("Printf"); //获取方法
mi2.Invoke(Activator.CreateInstance(t3),new object[] { "调用成功" }); //方法调用
Console.ReadLine();
}
}
public class AC
{
public string A;
public void ACP()
{
Console.WriteLine("ACP");
}
}
}
引入的程序集代码
namespace LeiKu
{
internal class Pub
{
public void Printf(string msg)
{
Console.BackgroundColor = ConsoleColor.Green; //改控制台背景元素
Console.WriteLine(msg); //打印信息
}
}
}
结果
![](https://i-blog.csdnimg.cn/blog_migrate/b63f23c51eeae740bf1983d0c0a0755a.png)
2.FieldInfo 字段
命名空间:System.Reflection
官网文档:地址链接
1)概述
发现字段的属性并提供对字段元数据的访问权限。
2)基本使用
获取字段 | t.GetField(字段名,访问权限BindingFlags | 静态非静态BindingFlags) |
读写字段 | fi.SetValue(对象,值) //给对象的字段赋值 object val = fi.GetValue(对象object) //获取对象的字段的值 //静态字段不需要对象,设置为null |
获取字段特性 | Attributes获取与此字段关联的特性 CustomAttributes获取包含此成员自定义属性的集合 |
3)实验
代码
using System.Reflection;
using Test;
namespace ConsoleApp1
{
public class Program
{
static void Main(String[] args)
{
// 获取类型
Type t = typeof(Test.TestC);
// 获取类型t中的name字段
FieldInfo fi = t.GetField("name"); //t.GetField("value")为null
// 实例化单例对象
object j = Activator.CreateInstance(t); //实例化
// 设置单例对象的name字段的值
fi.SetValue(j,"123");
// 打印
Console.WriteLine(fi.GetValue(j)); //打印name字段的值
Console.WriteLine(fi.Name); //打印fi对应的字段名称
}
}
}
//另一个命名空间
namespace Test
{
public class TestC
{
//字段
public string name = "cml";
public string value { get; set; }
}
}
结果
![](https://i-blog.csdnimg.cn/blog_migrate/3c29ada93697c194552a415485022dc5.png)
3.MemberInfo 成员
命名空间:System.Reflection
官网文档:地址链接
1)概述
获取有关成员属性的信息并提供对成员元数据的访问权限。
4.MethodInfo 方法
命名空间:System.Reflection
官网文档:地址链接
1)概述
发现方法的属性并提供对方法元数据的访问。
2)方法
返回值 | 方法名称 | 描述 |
object? | 当在派生类中重写时,调用具有给定参数的反射的方法或构造函数。 (继承自 MethodBase) 【调用方法/构造函数,该方法/构造函数具有参数,以object类型返回调用完后方法/构造函数返回的结果】 |
3)基本使用
获取方法 | t.GetMethod(String,BindingFlags,Binder,Type[],ParameterModifier[]) |
调用方法 | info.Invoke(Object,BindingFlags,Binder,Object[],CultureInfo) |
获取方法特性 | Attributes获取与此字段关联的特性 CustomAttributes获取包含此成员自定义属性的集合 |
4)实验
代码
using System.Reflection;
using Test;
namespace ConsoleApp1
{
public class Program
{
static void Main(String[] args)
{
//1.创建对象
Type t = typeof(Test.TestC); //获取类型
object j = Activator.CreateInstance(t,new object[] {"cml"}); //实例化
//2.获取目标类型的公共方法,方法名为AddValue
MethodInfo method = t.GetMethod("AddValue");
//3.目标对象调用method指定的方法
object msg = method.Invoke(j, new object[] { "Hello world !" }); //参数:实例化对象,目标方法参数
Console.WriteLine("msg:\t\t" + msg.ToString());
//4.打印对象的字段
TestC c = (TestC)j;
Console.WriteLine("c.value:\t" + c.value);
}
}
}
//另一个命名空间
namespace Test
{
public class TestC
{
//字段
public string value { get; set; }
//构造函数
public TestC()
{ }
public TestC(string v)
{
value = v;
}
//自定义函数
public string AddValue(string prefix)
{
return value + "+" + prefix; //将value与该方法传入参数拼接
}
}
}
结果
![](https://i-blog.csdnimg.cn/blog_migrate/e4899da591cc72f3cae5f29570cf013f.png)
5.BindingFlags
1)概述
命名空间:System.Reflection
官网文档:地址链接
搜索使用的枚举值 | |
BindingFlags (表示搜索的按位组合) | Public(公共)16 |
NonPublic(私有)32 | |
Static(静态)8 | |
Instance(非静态)4 | |
组合:eg私有的且非静态的NonPublic Instance | |
NonPublic | Instance 0010 0000 | 0000 0100 —————————— 0010 0000 |
BindingFlags详情相关可查看大佬文章:地址链接
二、其他相关
1.Activator
命名空间:System
官网文档:地址链接
1)概述
包含特定的方法,用以在本地或从远程 创建对象类型,或 获取对现有远程对象的引用。 此类不能被继承。
2)方法
返回值 | 方法名称 | 描述 |
object? | CreateInstance(Type t) | 使用目标类为Public且无参的构造函数实例化对象 |
object? | CreateInstance(Type t,params object[] args) | 使用目标类为Public且有参的构造函数实例化对象 |
object? | CreateInstance(Type t,bool nonPublic) | 公共或非公共默认构造函数都可以匹配,则为 true; 只有公共默认构造函数可以匹配,则为 false |
object? | CreateInstance(Type t, BindingFlags bindingAttr, Binder? binder,object[] args,CultureInfo) | 使用目标类为bindingAttr指定且有参构造函数实例化对象 |
【注意:目标类中不同的公共/非公共和有参/无参的构造函数应选择对应正确的方法实例化对象,否则"System.MissingMethodException:“Constructor on type 'Test.TestC' not found.”报错"。】
3)实验
代码 在运行时动态构造对象
using System.Reflection;
using Test;
namespace ConsoleApp1
{
public class Program
{
static void Main(String[] args)
{
//相当于Person p1=new Person ();
//通过反射,构造实例,返回的是一个object对象,接收存储
object p;
//1.通过反射,使用 公有、无参 构造函数实例化对象
p = Activator.CreateInstance(typeof(TestC));
TestC c1 = p as TestC; //转化
Console.WriteLine(c1.Value); //NULL证明调用了默认构造函数
//2.通过反射,使用 公有、有参 构造函数实例化对象
p = Activator.CreateInstance(typeof(TestC), new object[] {"123"});
TestC c2 = p as TestC;
Console.WriteLine(c2.Value); //123 证明调用了public、有参构造函数
//3.通过反射,使用 公有/非公有、无参 构造函数实例化对象
p = Activator.CreateInstance(typeof(TestC), false);
TestC c3 = p as TestC;
Console.WriteLine(c3.Value); //NULL证明调用了默认构造函数
//4.通过反射,调用 非共有、实例、有参 构造函数实例化对象----eg:私有的有参构造调用
p = Activator.CreateInstance(typeof(TestC), BindingFlags.NonPublic|BindingFlags.Instance, null, new object[] { "444","555" }, null);
TestC c4 = p as TestC;
Console.WriteLine(c4.Value); //444+555 证明调用了private、有两参构造函数
Console.Read();
}
}
}
//另一个命名空间
namespace Test
{
//一个简单的类,包含一个无参的构造器,一个有参数的构造器,一个GetValue的方法,一个Value属性
public class TestC
{
public string _value { get; set; }
public string Value
{
set
{
_value = value;
}
get
{
if (_value == null)
return "NULL";
else
return _value;
}
}
public TestC() //无参的构造函数
{ }
public TestC(string v) //带参的构造函数
{
_value = v;
}
private TestC(string v1, string v2)
{
_value = v1+"+"+v2;
}
}
}
结果
![](https://i-blog.csdnimg.cn/blog_migrate/b94939e7fedaf40935ed0baac42e1c29.png)
学习大佬文章 https://www.cnblogs.com/afei-24/p/6867986.html
和 https://blog.csdn.net/qq_45505703/article/details/102668091