程序集是经由编译器编译得到的,供进一步编译执行的中间产物。
在Windows系统中,它一般表现为后缀为.dll(库文件)或者.exe(可执行文件)的格式。
元数据是用来描述数据的数据。即程序中的类、类中的函数、变量等信息就是程序的元数据。有关程序以及类型的数据被称为元数据,他们保存在程序集中。
程序正在运行时,可以查看其它程序集或者自身的元数据。
一个运行的程序查看本身或者其它程序的元数据的行为叫做反射。
说的通俗点就是在程序运行时,通过反射可以得到其它程序集或者自己程序集代码的各种信息,包括类、函数、变量等来实例化它们,执行它们,操作它们。
因为反射可以在程序编译后获得信息,所以它提高了程序的拓展性和灵活性。
1、程序运行时得到所有元数据,包括元数据的特性。
2、程序运行时实例化对象,操作对象。
3、程序运行时创建新的对象,用这些对象执行任务。
C#反射的语法主要包括三个类:分别是Type、Assembly和Activator。
首先创建一个测试类方便测试:
class Test
{
private int i = 1;
public int j = 0;
public string str = "123";
public Test()
{
}
public Test(int i)
{
this.i = i;
}
public Test(int i ,string str) : this(i)
{
this.str = str;
}
public void Speak()
{
Console.WriteLine(i);
}
}
Type是类的信息类,它是反射功能的挤出,访问元数据的主要方式。
使用Type的成员获取有关类型(如构造函数、方法、字段、属性和类的事件等)声明的信息。
1、object中的GetType()可以获取对象的Type
int a = 42;
Type type = a.GetType();
Console.WriteLine(type);
打印结果是System.Int32即为a的类型
2、通过typeof关键字,传入类名可以获取对象的Type
Type type2 = typeof(int);
Console.WriteLine(type2);
打印结果是System.Int32即为int的类型
3、通过类的名字可以获取类型,这里要注意类名必须包含明明空间,否则会找不到。
Type type3 = Type.GetType("Int32");
Console.WriteLine(type3);
Type type4 = Type.GetType("System.Int32");
Console.WriteLine(type4);
打印结果type3为空,type4为System.Int32
可以通过Type得到类型所在程序集信息。
Console.WriteLine(type.Assembly);
Console.WriteLine(type2.Assembly);
Console.WriteLine(type4.Assembly);
需要引用命名空间using System.Reflection;
1、首先得到Type
Type t = typeof(Test);
这里有一个小的知识点,通过typeof获取Type一般是在能够明确得到类名的情况下,即获取同一程序集下的类的Type的时候;如果是不同程序集下的类,可以通过Type.GetType()获取。
2、然后得到所有公共成员
MemberInfo[] infos = t.GetMembers();
for (int i = 0; i < infos.Length; i++)
{
Console.WriteLine(infos[i]);
}
MemberInfo即该类型下公共成员容器。
1、获取所有构造函数
获取构造函数可以通过GetConstructor和GetConstructors获得,通过ConstructorInfo接收。
ConstructorInfo[] ctors = t.GetConstructors();
for (int i = 0; i < ctors.Length; i++)
{
Console.WriteLine(ctors[i]);
}
2、获取其中一个构造函数并执行。
2.1:得构造函数需要传入Type数组,数组中内容按顺序是参数类型。
2.2:执行构造函数,需要传入object数组,表示按顺序传入的参数。
无参构造函数:
//得到无参构造函数
ConstructorInfo info = t.GetConstructor(new Type[0]);
//执行无参构造 无参构造没有参数传null
Test obj = info.Invoke(null) as Test;
Console.WriteLine(obj.j);
有参构造函数:
//得到有参构造函数
ConstructorInfo info2 = t.GetConstructor(new Type[] { typeof(int), typeof(string) });
//执行有参构造函数
obj = info2.Invoke(new object[] { 2 ,"asd"}) as Test;
Console.WriteLine(obj.str);
1、得到所有成员变量
FieldInfo[] fileldInfos = t.GetFields();
for (int i = 0; i < fileldInfos.Length; i++)
{
Console.WriteLine(fileldInfos[i]);
}
2、得到指定名称的公共成员变量
FieldInfo infoJ = t.GetField("j");
Console.WriteLine(infoJ);
3、通过反射获取和设置对象的值
获取和设置可以分别通过GetValue和SetValue进行,这里我们使用上面得到的obj来测试。
3.1、通过反射获取对象的某个变量的值
Console.WriteLine(infoJ.GetValue(obj));
3.2、通过反射设置指定对象的某个变量的值
infoJ.SetValue(obj, 100);
Console.WriteLine(infoJ.GetValue(obj));
获取成员方法可以通过Type类中的GetMethod方法来得到类中的方法,MethodInfo是方法的反射信息。
如果存在方法重载,用Type数组表示参数类型。
这里为了方便测试,我们使用string类来查看里面的方法。
Type strType = typeof(string);
//得到所有函数
MethodInfo[] methods = strType.GetMethods();
for (int i = 0; i < methods.Length; i++)
{
Console.WriteLine(methods[i]);
}
//得到指定的Substring函数(int,int)重载
MethodInfo method = strType.GetMethod("Substring", new Type[] { typeof(int), typeof(int) });
Console.WriteLine(method);
调用方法(注意:如果是静态方法,Invoke中的第一个参数传null即可)
string str = "Hello,World!";
//第一个参数相当于哪个对象要执行这个成员方法,
object result = method.Invoke(str, new object[] { 7, 5 });
Console.WriteLine(result);
获取其他成员变量大同小异,这里只提示方法并不一一赘述。
1、得到枚举:GetEnumName、GetEnumNames
2、得到事件:GetEvent、GetEvents
3、得到接口:GetInterface、GetInterfaces
4、得到属性:GetProperty、GetProtertys
Activator是用于快速实例化对象的类,用于将Type对象快捷实例化为对象。
先得到Type,然后快速实例化一个对象。
Activator.CreateInstance默认调用无参构造函数。
1、无参构造函数:
Type test = typeof(Test);
Test testObj = Activator.CreateInstance(test) as Test;
Console.WriteLine(testObj.str);
2、有参构造函数:
如果要调用有参构造函数,在后面一次添加参数即可。
testObj = Activator.CreateInstance(test, 99) as Test;
testObj = Activator.CreateInstance(test, 55, "11111") as Test;
Console.WriteLine(testObj.str);
Assembly类其实就是程序集类。主要用来加载其他程序集,加载后才能用Type来使用其他程序集中的信息,如果想要使用不是自己程序集中的内容,需要先加载程序集(比如dll文件)。
三种加载程序集的函数:
1、一般用来加载同一文件下的其他程序集
Assembly assembly = Assembly.Load(“程序集名称”);
2、一般用来加载不再同一文件下的其他程序集
Assembly assembly = Assembly.LoadFrom(“包含程序集清单的文件的名称或路径”);
Assembly assembly = Assembly.LoadFile(“要加载的文件的完全限定路径”);
使用方法:
1、首先加载一个指定程序集:
Assembly assembly = Assembly.LoadFrom (@"C:\Users\01\Desktop\ConsoleApp1\TestDLL\bin\Debug\TestDLL.dll");
Type[] types = assembly.GetTypes();
for (int i = 0; i < types.Length; i++)
{
Console.WriteLine(types[i]);
}
2、再加载程序集中的一个类对象,之后才能使用反射
//得到dll中的Class1类
Type c = assembly.GetType("TestDLL.Class1");
object o = Activator.CreateInstance(c);
//调用Class1类中的Speak方法
MethodInfo speak = c.GetMethod("Speak");
speak.Invoke(o,null);
其中TestDLL.dll为自己封装的dll,内容如下:
namespace TestDLL
{
public class Class1
{
public int i = 0;
public string str = "123";
public void Speak()
{
Console.WriteLine("speak");
}
}
}