【C#反射】开篇

微软官方教程:https://docs.microsoft.com/zh-cn/dotnet/framework/reflection-and-codedom/viewing-type-information

元編程(英語:Metaprogramming),又譯超編程,是指某类计算机程序的编写,这类计算机程序编写或者操纵其它程序(或者自身)作为它们的資料,或者在运行时完成部分本应在编译时完成的工作。多数情况下,与手工编写全部代码相比,程序员可以获得更高的工作效率,或者给与程序更大的灵活度去处理新的情形而无需重新编译。

编写元程序的语言称之为元语言。被操纵的程序的语言称之为「目标语言」。一门编程语言同时也是自身的元语言的能力称之为「反射」或者「自反」。

反射是促进元编程的一种很有价值的语言特性。把编程语言自身作为一級資料類型(如LISPForthRebol)也很有用。支持泛型编程的语言也使用元编程能力。

元编程通常通过两种方式实现。一种是通过应用程序编程接口(APIs)将运行时引擎的内部信息暴露于编程代码。另一种是动态执行包含编程命令的字符串表达式。因此,“程序能够编写程序”。虽然两种方式都能用于同一种语言,但大多数语言趋向于偏向其中一种。

个人理解元编程

C# 目前的反射只能对现在类型进行操作,还不能创建新的类型或编辑现有字段的类型信息。
反射获取的字段、属性、方法 如一个万能钥匙,可以用它操控所有的类型实例。C#泛型 反射都是元编程的能力

 

什么是反射

在程序运行时,动态获取 程序集, 类型(class,interface)和类型的成员信息(方法,字段,属性等)。
在程序运行时,动态创建 类型实例, 以及调用和方法 动态创建出来的 类型实例的成员。net core 一个程序运行起来以后,有一个AppDomain,在这个AppDomain中放了我们用到的所有assembly。

 反射机制对应设计模式中的策略模式。

 

 

 

 

 反射的作用:
1. 可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现 有对象中获取类型
2. 应用程序需要在运行时从某个特定的程序集中载入一个特定的类型,以便实现某个任务时可以用到反射。
3. 反射主要应用与类库,这些类库需要知道一个类型的定义,以便提供更多的功能。框架(Spring .net/ .Net MVC等)

 4、破坏单例模式 反射可以突破访问限制,直接调用私有构造函数

5、依赖注入

6、插件编程

7、序列化

 C#反射的局限

反射不能修改readonly、只读属性、const字段、字符串拘留池  。

当修改readonly字段时候出现异常 System.FieldAccessException:“Cannot set initonly static field 'data' after type 'Program1' is initialized.”

修改只读属性的时候出现:Setting static readonly properties fails 异常。

using System.Reflection;

mod();
 

static void mod()
{
    Program1 ss = new();
    var fieldInfo = typeof(Program1).GetField("data", BindingFlags.Static | BindingFlags.NonPublic);
    // fieldInfo.SetValue(ss, "MOD");

    fieldInfo.SetValue(ss, "MOD");//出现异常 System.FieldAccessException:“Cannot set initonly static field 'data' after type 'Program1' is initialized.”
}
class Program1
{
    static readonly string data = "df";
    //
    //这句运行正常  static   string data = "df";

}

 

反射在在设计模式实现中的使用


  采用反射技术可以简化工厂的实现。 

  (1)工厂方法:通过反射可以将需要实现的子类名称传递给工厂方法,这样无须在子类中实现类的实例化。 

  (2)抽象工厂:使用反射可以减少抽象工厂的子类。 

  采用反射技术可以简化工厂代码的复杂程度,在.NET项目中,采用反射技术的工厂已经基本代替了工厂方法。 

  采用反射技术可以极大地简化对象的生成,对以下设计模式的实现也有很大影响。 

  (1)命令模式:可以采用命令的类型名称作为参数直接获得命令的实例,并且可以动态执行命令。 

  (2)享元模式:采用反射技术实例化享元可以简化享元工厂

 (3).NET Framework提供了两种方法来访问类型上的元数据:命名空间中System.Reflection提供的反射 API 和TypeDescriptor类。 反射是适用于所有类型的通用机制,因为它的基础是在根Object类的方法中建立的GetType。 它为类型返回的信息不可扩展,因为它无法在目标类型的编译后对其进行修改。 

反射用到命名空间

System.Reflection
    
System.Type
   
System.Reflection.Assembly
System.Reflection.Emit

System.Type 类:通过这个类可以访问任何给定数据类型的信息。
对于反射起着核心的作用。但它是一个抽象的基类,Type有与每种数据类型对应的派生类,我们使用这个派生类的对象的方法、字段、属性来查找有关该类型的所有信息。


System.Reflection.Assembly类:它可以用于访问给定程序集的信息,或者把这个程序集加载到程序中。

System.Reflection.Emit命名空间的类提供了一种特殊形式的反射,可以在运行时构造类型。

System.Reflection


 

 

 

 

 

 

 

 

MemberInfo 包含以下类型:

MemberTypes

 [Flags]
    public enum MemberTypes
    {
Constructor = 1,
Event = 2,
Field = 4,
Method = 8,
Property = 16,
TypeInfo = 32,
Custom = 64,
NestedType = 128,
All = 191
 }
}

 TypeInfo和Type的区别与选择

Type 类:表示类型声明(类型的引用):类类型、接口类型、数组类型、值类型、枚举类型、类型参数、泛型类型定义,以及开放或封闭构造的泛型类型。。
TypeInfo 类:表示类类型、接口类型、数组类型、值类型、枚举类型、类型参数、泛型类型定义,以及开放或封闭构造的泛型类型的类型声明。

TypeInfo对象表示类型定义本身,而 Type 对象表示对类型定义的引用。 获取 TypeInfo 对象将强制加载包含该类型的程序集。 相比之下,你可以操作 Type 对象,而无需运行时加载它们引用的程序集。

TypeInfo出现于.net framework 4.5之后,这次调整用于区分两个概念:“reference”和“definition”。
    reference is a shallow representation of something
    definition is  a rich representation of something
例如System.Reflection.Assembly就代表了一个“definition” ,而System.Reflection.AssemblyName就代表了一个“reference”
在未区分这两种概念之前,System.Type是这两种概念的混合,通过一个Type实例,既可以获得该类型的“Name、Assembly、……”,也可以获得该类型的“NestTypes、Fields、Properties、Methods、Constructors、Events……”。这也导致了当我们获得一个Type的时候,就把它全部的信息都加载到了内存中,但是很多情况下,这并不是我们想要看到的。举例如下:
更多相关内容请查看:https://blog.csdn.net/fengsocool/article/details/85927995

public MyClass:BaseClass{
 
}
 
//获得MyClass的类型对象
 
Type t=MyClass.GetType();

 在.net framework 4中,获得类型对象时,同时获得了基类(BaseClass)的类型对象,同时包括基类对象的“reference”和“definition”,因此需要把基类所在的程序集也加载进来。
在.net framework 4.5中,如下代码:
Type baseType = MyClass.GetType().GetTypeInfo().BaseType;
 在获得类型对象时,只获得了基类的"reference"
 

 

反射经常用方法

 

#define DEBUG  ////C# 的宏定义必须出现在所有代码之,告诉编译器哪些特性要编译IL代码
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;

namespace TestReflection
{
    public delegate void NoParametMethodEventHandler(object o, EventArgs e);
    public delegate void methodBing(string x);

    public class UnFinsedAttribute:Attribute
    {


    }
    public interface IPlay { void Play(); }
    [UnFinsed]
    class MyType:IPlay
    {
     
        public event NoParametMethodEventHandler DianMing=delegate { };
        Int32 myField;
        //构造函数
        public MyType()
        {

        }
        public MyType(ref Int32 x) { x *= 5; }
        public MyType(int x) { x = 5; }
        public MyType(Int32 x, int c) { x *= 5; }
        public MyType(Int32[] x, int c) { c *= 5; }
        public override String ToString()=>myField.ToString();
        public void Pritn(string x) => Console.WriteLine("Pritn(string x)");
        public void Pritn(string x,int s) => Console.WriteLine("Pritn(string x,int s)");
        //属性
        public Int32 MyProp
        {
            get { return myField; }
            set
            {
                if (value < 1)
                    throw new ArgumentOutOfRangeException("value", value, "value must be > 0");
                myField = value;
            }
        }

        public string Address { get; set; }
        public string Name => "自定义类";

        //索引
    
        public int this[string i]
        {
            get { return 1; }
            set { }
        }
        public int this[int i]
        {
            get { return 1; }
            set { }
        }
        //事件触发器
        public void ClickMe()
        {
            DianMing += Huida;
            DianMing.Invoke(new object(),new EventArgs());
        }
        //事件处理器
        public void Huida(object o, EventArgs e)
        {
            Console.WriteLine($"{Name}到");
        }

        public void Play()
        {
            Console.WriteLine("接口方法");
        }
        public void BingMethodToDelegat(string x)
        {
            Console.WriteLine($"你调用了动态方法BingMethodToDelegat方法的参数x:{x}  该方法调用了实例属性MyProp:{MyProp}");
        }
        public static void StaticMethod(string x)
        {

            Console.WriteLine("你调用了静态方法StaticMethod");
        }
    }

    class MyApp
    {
        static void Main()
        {

            Type type = typeof(MyType);
            Type parameterType = typeof(int);
            //====================构造函数获取

            // Create an instance of a type.


            Object[] args = new Object[] { 8 };
            object obj = Activator.CreateInstance(type);
            //创建实例
            Console.WriteLine("=========创建实例===============");
            //创建泛型
            Type open = typeof(Dictionary<,>);//创建泛型类型的实例,首先要获取对应的开放类型(Open type)的引用,然后调用Type类型的MakeGenericType方法
            //传入一个包含泛型参数的数组即可获取一个封闭类型(Closed Type).使用该封闭类型,调用Activator接受Type参数的某个方法既可以构造出具体的实例。
            Type closeType = open.MakeGenericType(typeof(String), typeof(object)); 

            object obj1 = Activator.CreateInstance(closeType);//方法一、Activator创建实例 
            ConstructorInfo constructorCreature = type.GetConstructor(Array.Empty<Type>());
            ConstructorInfo constructor6 = type.GetConstructor(new[] { typeof(int) });
            var obj2 = constructor6.Invoke(new[]{(Object)3});//方法二、构造创建实例 
            Object obj3= type.Assembly.CreateInstance("MyType");//方法三、Assembly创建实例
            Object obj4 = type.InvokeMember(null, BindingFlags.CreateInstance, null, null, new Object[] { 8 });//方法四、InvokeMember 创建实例

            Type refParameter = parameterType.MakeByRefType(); //参数 MyType(ref Int32 x);
            Type arrayParameter = parameterType.MakeArrayType(2); //参数数组int[]= new int[2];
            Type parameterGeneric = typeof(List<>);//泛型参数
            Type parameterGeneric2 = parameterGeneric.MakeGenericType(typeof(int));//list<int>


            //获取字段
            Console.WriteLine("=========获取字段===============");
            FieldInfo field = type.GetField("myField",BindingFlags.NonPublic|BindingFlags.Instance);
            field.SetValue(obj, 1);//设置私有字段的
            Console.WriteLine(field.GetValue(obj));//获取私有字段
            Console.WriteLine(field.FieldType);//获取字段类型


            //获取构造函数  
            Console.WriteLine("=========获取构造函数 ===========");
            ConstructorInfo constructorInfo1 = type.GetConstructor(new Type[0]);//过时 获取无参构造函数 参数是无 new Type[0] 表示空数组
            ConstructorInfo constructorInfo2 = type.GetConstructor(Array.Empty<Type>());//获取无参构造函数 参数是无 new Type[0] 表示空数组
            ConstructorInfo constructorInfo3 = type.GetConstructor(new Type[] { });// 过时 获取无参构造函数 参数是无(new Type[] { }]表示空数组
            ConstructorInfo constructorInfo4 = type.GetConstructor(Type.EmptyTypes); //获取无参构造函数 这个才是最正确符合逻辑的写法
            ConstructorInfo constructorInfo5 = type.GetConstructor(new[] { refParameter });//MyType(ref Int32 x)
            ConstructorInfo constructorInfo6 = type.GetConstructor(new[] { typeof(int) });//MyType(Int32 x)
            ConstructorInfo constructorInfo7 = type.GetConstructor(new[] { typeof(int), typeof(int) });//MyType(Int32 x,int c)
            ConstructorInfo constructorInfo8 = type.GetConstructor(new[] { typeof(int[]), typeof(int) });//MyType(Int32 x)
            ConstructorInfo[] constructorInfoAll = type.GetConstructors();//获取所有构造函数


            //获取属性
            Console.WriteLine("========获取属性===============");
            PropertyInfo propertyInfo1 = type.GetProperty("Name");
            PropertyInfo propertyInfo2 = type.GetProperty("Address");
            PropertyInfo[] propertyInfoAll = type.GetProperties();//获取所有的属性,把索引也获取进去了 索引是有参属性,名字item
            Console.WriteLine(propertyInfo2.PropertyType);//属性的类型

           
            Console.WriteLine(propertyInfo1.GetValue(obj));
            //   propertyInfo1.SetValue(obj, "haha");//错误的,因为没有set属性,所以无法设置,set就是set函数
            Console.WriteLine(propertyInfo1.GetValue(obj));

            Console.WriteLine(propertyInfo2.GetValue(obj));
            propertyInfo2.SetValue(obj, "haha");//正确的,因为有set属性。
            Console.WriteLine(propertyInfo2.GetValue(obj));

            //获取索引
            Console.WriteLine("=========获取索引===============");
            PropertyInfo propertyInfo3 = type.GetProperty("Item",typeof(int),new[] { typeof(int) });//获取索引,索引的名字都是Item发,获取返回值是int,参数是int的属性。


            //获取调用方法
            Console.WriteLine("=========获取调用方法============");
            MethodInfo method1 = type.GetMethod("ToString");
            MethodInfo method2 = type.GetMethod("Pritn",new[] { typeof(string) });//获取一个参数的方法
            MethodInfo method3 = type.GetMethod("Pritn",new[] {typeof(string),typeof(int)});//获取2个参数的方法
            MethodInfo method4 = type.GetMethod("ClickMe");
            MethodInfo method5 = type.GetMethod("Huida",new[] { typeof(object), typeof(EventArgs) });
            MethodInfo method6 = type.GetMethod("StaticMethod", new[] { typeof(string) });//获取一个参数的方法

            MemberInfo[] methodAll = type.GetMethods();
            method4.Invoke(obj, null);//执行方法
           
            



            //事件
            Console.WriteLine("=========事件===============");
            NoParametMethodEventHandler de = Huida2 ;
            EventInfo @event = type.GetEvent("DianMing");//
            @event.AddEventHandler(obj, de);//订阅事件          
            void Huida2(object o, EventArgs e)  //处理器
            {
                Console.WriteLine("manin到");
            }
            method4.Invoke(obj, null);//执行方法
            Console.WriteLine(@event.EventHandlerType.Name);//事件类型

            //接口
            Console.WriteLine("=========接口===============");
            Type interFace = type.GetInterface("IPlay");


            //委托绑定 
            Console.WriteLine("=========委托绑定===============");
            Delegate methodDelegat1 = Delegate.CreateDelegate(typeof(methodBing), obj, "BingMethodToDelegat");//调用实例方法,必须绑定实例
            methodDelegat1.DynamicInvoke("methodDelegat1");//实例方法要绑定实例使用,要不很容易报错,因为方法内部会调用很多内部成员

            Delegate methodDelegat2 = Delegate.CreateDelegate(typeof(methodBing), null, method6);//调用了静态方法,静态不依附实例
            methodDelegat2.DynamicInvoke("methodDelegat2");

            //获取特性
        var typeAttribute=  type.GetCustomAttribute(typeof(UnFinsedAttribute));
          

        }
        
       
    }

}

 反射为什么慢?

使用反射来调用类型或者触发方法,或者访问一个字段或者属性时clr 需要做更多的工作:校验参数,检查权限等等,所以速度是非常慢的。

posted @ 2021-10-29 16:26  小林野夫  阅读(383)  评论(0编辑  收藏  举报
原文链接:https://www.cnblogs.com/cdaniu/