一、什么是C#反射(Reflection)
定义:属性提供了一种将元数据或声明性信息与代码(程序集、类型、方法、属性等)关联的强大方法。
当一个属性与一个程序实体相关联后,可以使用一种叫做 反射 .
这是.Net中获取运行时类型信息的方式,.Net的应用程序包含以下几个部分:
‘程序集(Assembly)’、
‘模块(Module)’、
‘类型(class)’,
而反射提供一种编程的方式,让程序员可以在程序运行期获得这几个组成部分的相关信息,例如:
Assembly类可以获得正在运行的装配件信息,也可以动态的加载装配件,以及在装配件中查找类型信息,并创建该类型的实例。
Type类可以获得对象的类型信息,此信息包含对象的所有要素:方法、构造器、属性等等,通过Type类可以得到这些要素的信息,并且调用之。
MethodInfo类包含方法的信息,通过这个类可以得到方法的名称、参数、返回值等,并且可以调用之。
诸如此类,还有FieldInfo、EventInfo等等,这些类都包含在System.Reflection命名空间下。
在讲反射的应用之前先讲清楚命名空间与装配件的关系:
命名空间:命名空间是程序设计者命名的内存区域,程序设计者根据需指定一些有名字的空间域,
把一些全局实体分别存放到各个命名空间中,从而与其他全局实体分隔开。
装配件:装配件也可以叫做程序集,是.Net应用程序执行的最小单位,编译出来的.dll、.exe都是装配件。
装配件和命名空间的关系不是一一对应,也不互相包含,一个装配件里面可以有多个命名空间,一个命名空间也可以在多个装配件中存在
类比成民族就好理解多了,比如有人是汉族、有人是回族,有人住在北京、有人住在上海;那么北京有汉族人,也有回族人,上海有汉族人,也有回族人,这是不矛盾的。
二、反射的使用,反射有什么用
编译中分为动态编译和静态编译,静态编译是在编译中确定类型,绑定对象,而动态编译是在运行中确定类型,绑定对象
反射就是一种可以动态创建对象、绑定对象的手段。但是缺点是性能上不如静态绑定
既然在开发时就能够写好代码,干嘛还放到运行期去做,其实不然,这种操作提高了程序的灵活性和扩展性
比如很多软件开发者喜欢在自己的软件中留下一些接口,其他人可以编写一些插件来扩充软件的功能,
比如我有一个媒体播放器,我希望以后可以很方便的扩展识别的格式,那么我声明一个接口:
public interface IMediaFormat { string Extension {get;} Decoder GetDecoder(); }
这个接口中包含一个Extension属性,这个属性返回支持的扩展名,另一个方法返回一个解码器的对象
(这里我假设了一个Decoder的类,这个类提供把文件流解码的功能,扩展插件可以派生之),
通过解码器对象我就可以解释文件流。那么我规定所有的解码插件都必须派生一个解码器,并且实现这个接口,
在GetDecoder方法中返回解码器对象,并且将其类型的名称配置到我的配置文件里面。
这样的话,我就不需要在开发播放器的时侯知道将来扩展的格式的类型,只需要从配置文件中获取现在所有解码器的类型名称,而动态的创建媒体格式的对象,将其转换为IMediaFormat接口来使用。
如何通过反射获取类型,以下是示例:
static void Main(string[] args) { //通过对象获取到这个对象所属类的Type对象 TestClass c=new TestClass (); //Type t = c.GetType (); //通过Type类中的静态方法GetType获取到类的Type对象 //Type t = Type.GetType("TestClass"); //3.通过typeof关键字获取到类的Type对象 Type t = typeof(TestClass); Console.WriteLine(t.Name);//获取类名(不带命名空间) Console.WriteLine(t.FullName);//获取类名(带命名空间) Console.WriteLine(t.Assembly);//获取程序集 Console.WriteLine(t.BaseType);//获取基类类型 Console.WriteLine("----------获取类中字段"); var fileds = t.GetFields(); foreach (var f in fileds) { Console.WriteLine(f.Name); } Console.WriteLine("----------获取类中属性"); var properties = t.GetProperties(); foreach (var p in properties) { Console.WriteLine(p.Name); } Console.WriteLine("----------获取类中方法"); var methods = t.GetMethods(); foreach (var m in methods) { Console.WriteLine(m.Name); } Console.WriteLine("----------获取类中成员"); var members = t.GetMembers(); foreach (var m in members) { Console.WriteLine(m.Name); } Console.WriteLine("----------获取类中嵌套类"); var nesteds = t.GetNestedTypes(); foreach (var nested in nesteds) { Console.WriteLine(nested.Name); } Console.WriteLine("----------获取类中构造函数"); var constructors = t.GetConstructors(); foreach (var constructor in constructors) { Console.WriteLine(constructor.Name); } Console.WriteLine("----------获取类中事件"); var events = t.GetEvents(); foreach(var item in events) { Console.WriteLine(item.Name); } Console.WriteLine("----------获取类继承的接口"); var faces = t.GetInterfaces(); foreach (var item in faces) { Console.WriteLine(item.Name); } Console.WriteLine("----------获取类继承的父类"); var father = t.BaseType; Console.WriteLine(father.Name); //获取所有程序集 var allAssemblies = AppDomain.CurrentDomain.GetAssemblies(); Console.ReadKey(); } public interface TestFace { string Name { get; } Decoder GetDecoder(); } public class TestFatherClass { } public class TestClass : TestFatherClass,TestFace { public delegate void Foo(); public event Foo OnFoo; public string str; public int num { get; set; } public string Name => throw new NotImplementedException(); public void Fun() { } public Decoder GetDecoder() { throw new NotImplementedException(); } public class TestNestedClass { } }
接下来就是根据动态获取的类创建对于的实例了:
static void Main(string[] args) { //通过对象获取到这个对象所属类的Type对象 //TestClass c=new TestClass (); //Type t = c.GetType (); //通过Type类中的静态方法GetType获取到类的Type对象 //Type t = Type.GetType("TestClass"); //3.通过typeof关键字获取到类的Type对象 Type t = typeof(TestClass); //System.Activator提供了方法来根据类型动态创建对象 object c = Activator.CreateInstance(typeof(TestClass), "hello world"); //调用之前需要转换一下类型 Console.WriteLine(((TestClass)c).str); //动态调用方法 ((TestClass)c).Fun(); //调用类中的委托 // 获取类型,实际上这里也可以直接用typeof来获取类型 TestClass obj = new TestClass(""); Type type = Type.GetType("ConsoleApp1.TestClass"); TestDelegate method = (TestDelegate)Delegate.CreateDelegate(type, obj, "GetValue"); String returnValue = method("hello"); Console.WriteLine(returnValue); Console.ReadKey(); } public interface TestFace { string Name { get; } Decoder GetDecoder(); } public class TestFatherClass { } delegate string TestDelegate(string value); public class TestClass : TestFatherClass,TestFace { public string GetValue(string value) { return value; } public delegate void Foo(string value); public event Foo OnFoo; public string str; public TestClass(string str) { this.str= str; } public int num { get; set; } public string Name => throw new NotImplementedException(); public void Fun() { Console.WriteLine("我被调用啦!"); } public Decoder GetDecoder() { throw new NotImplementedException(); } public class TestNestedClass { } }
当然也有标准的写法:
//通过构造函数创建实例
Type t = typeof(TestClass);
Type[] paramTypes = new Type[1] { typeof(string) };
var info = t.GetConstructor(paramTypes);
object[] param = new object[1] { "hello world" };
var o = info.Invoke(param);
Console.WriteLine(((TestClass)o).str);
//反射赋值 var data = Activator.CreateInstance(Type.GetType("TestFatherClass")); var finfo = data.GetType().GetField("str"); finfo.SetValue(data, "Hello World!!"); Console.WriteLine(((TestFatherClass)data).str); public class TestFatherClass { public string str; }
//System.Activator提供了方法来根据类型动态创建对象 object c = Activator.CreateInstance(typeof(TestClass), "hello world"); //调用之前需要转换一下类型 Console.WriteLine(((TestClass)c).str); //动态调用方法 var m1 = c.GetType().GetMethod("Fun"); m1.Invoke(data, new object[] { "参数" });
三、反射实例:使用接口和反射制作热插拔动态加载类库
预计实现效果:
启动程序后打开插件目录,将准备好的插件dll复制到插件目录下,程序可动态加载该dll的功能

可以看到,当这个目录下面啥都没有时程序没有任何功能

当把准备好的插件放入指定目录下,程序就会出现插件的功能
代码实现如下:
oh,fuck!快晚上一点了,以后再写