c#学习笔记-------------------反射

发布时间 2023-07-03 00:51:48作者: 三流程序媛

一、什么是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!快晚上一点了,以后再写