zoukankan      html  css  js  c++  java
  • 程序集加载和反射

    一、程序集加载

    1,根据程序集名称查找程序集

        public class Assembly
        {
            public static Assembly Load(AssemblyName assemblyRef);
            public static Assembly Load(string assemblyString);
            //未列出不常用的Load重载
        }
    Assembly.Load("ConsoleApplication2");,

    2,根据程序集文件路径名(包含扩展名)查找程序集

        public class Assembly
        {
            public static Assembly LoadFrom(string path);
            //未列出不常用的LoadFrom重载
        }
    Assembly.LoadFrom("ConsoleApplication2.exe");

    3,加载程序集时,确保程序集中的任何代码不会执行

        public class Assembly
        {
            public static Assembly ReflectionOnlyLoadFrom(string assemblyFile);
            public static Assembly ReflectionOnlyLoad(string assemblyFile);
            //未列出不常用的ReflectionOnlyLoad重载
        }

    二、反射的性能

    1,反射的两个缺点
    ①反射造成编译时无法保证类型安全性。由于反射严重依赖字符串,所以会丧失编译时的类型安全性。例如,执行Type.GetType("int");要求通过反射在程序集中查找名为“int”类型,代理会通过编译,但是在运行时会返回null,因为CLR只知“System.Int32”不知道“int”
    ②反射速度慢。使用反射是,类型及其成员的名称在编译时未知。你需要用字符串名称标识每个类型及其成员,然后再运行时发现他们。也就是说,使用System.Reflection命名空间中的类型扫描程序集的元数据时,反射机制会不停地执行字符串搜索。通常,字符串搜索执行的是不区分大小写的比较,这会进一步影响速度
    ③用反射调用方法时,首先必须将实参打包成数组。在内部,反射必须将这些实参解包到线程栈上。此外,在调用方法前,CLR必须检查实参具有正确的数据类型。最后,CLR必须确保调用者有正确的安全权限来访问被调用的成员


    2,利用一下两种技术之一开发应用程序来动态发现和构造类型实例
    ①让类型从编译时一直的基类型派生。在运行时构造派生类型的实例,将对它的引用方到基类的变量中(利用转型),再调用基类定义的虚方法
    ②让类型实现编译时已知的接口。在运行时构造类型的实例,将对它的引用放到接口类型的变量中利用转型(),在调用接口定义的方法


    3,发现程序集中定义的类型

    namespace ConsoleApplication2
    {
        public class Program
        {
            static void Main(string[] args)
            {
                //显示将程序集加载到这个AppDomain中
                Assembly a = Assembly.Load("ConsoleApplication2");
                foreach (var exportedType in a.ExportedTypes)
                {
                    //显示访问修饰符为public的类型
                    Console.WriteLine(exportedType.FullName);
                }
                Console.ReadKey();
            }
        }
    }

    4,类型对象的准确含义

    ①System.Type类型提供了静态GetType方法的几个重载版本。所有版本都接受一个String参数。字符串必须指定类型的全名(包括它的命名空间)。找到,则返回对恰当的Type对象的引用。没有找到,则就检查MSCorLib.dll定义类型,如果还是没找到,则为null或抛出System.TyprLoadException。可向GetType传递限定程序集的类型字符创,比如“System.Int32,mscorlib,Version=4.0.0.0,Culture=neutral,PublicKeyToken=677a5c61934e098”,GetType方法会在指定程序集中查找类型(如果有必要会加载程序集)

    ②System.Type类型提供静态ReflectionOnlyGetType方法。该方法与上一条提到的GetType方法在行为上相似,只是类型会以“仅反射”的方式加载,不能执行

    ③System.TypeInfo类型提供了实例成员DeclaredNestedTypes和GetDeclaredNestedType

    ④System.Reflection.Assemble类型提供了实例成员GetType,DefinedTypes和ExportedTypes

    var t = Type.GetType("ConsoleApplication2.Program");
    Console.WriteLine(t == null ? "未找到" : "找到");
            private static void SomeMethod(object o)
            {
                //GetType在运行时返回对象的类型(晚期绑定)
                //typeof返回指定的类型(早起绑定)
                if (o.GetType() == typeof (FileInfo))
                {
                }
            }

    上述代码的第一个if语句检查变量o是否引用了FileInfo类型的对象;它不检查o是否引用从FileInfo类型派生的对象(使用is/as操作符会检查派生对象

    使用Type对象的GetTypeInfo方法获取对类型的更多控制

    5,构造类型的实例
    ①System.Activator的CreateInstance方法
    ②System.Activator的CreateInstanceFrom方法
    他与CreateInstance的行为相似,只是必须通过字符串来指出类型及其程序集。程序集用Assembly的LoadFrom(而非Load)方法加载到调用AppDomain中。由于都不接受Type参数,所以返回的都是一个ObjectHandle对象引用,必须调用Unwrap方法进行具体化
    ③System. AppDomain的方法
    AppDomain类型提供了4个用于构造类型实例的实例方法(每个都有几个重载版本),包括CreateInstance,CreateInstanceAndUnwrap,CreateInstanceForm和CreateInstanceFromAndUnwrap。这些方法的行为和Activator类的方法相似,区别在于它们都是实例方法,允许指定哪个AppDomain中构造对象。另外,带Unwrap后缀的方法还能简化操作,必须执行额外的方法调用
    ④System.Reflection.ConstructorInfo的Invoke实例方法
    使用一个Type对象引用,可以绑定到一个特定的构造器,并获取对构造器的ConstructorInfo对象的引用。然后,可利用ConstructorInfo对象引用来调用它的Incoke方法。类型总是在调用AppDomain中创建,返回的是对新对象的引用
    ⑤创建数组
    创建数组需要调用Array的静态CreateInstance方法
    ⑥创建委托
    创建委托需要调用MethodInfo的静态CreateDelegate方法
    ⑦构造泛型类型

            static void Main(string[] args)
            {
                //获取对泛型类型的类型对象引用
                var openType = typeof (Dictionary<,>);
                //使用TKey=String、TValue=Int32封闭泛型类型
                var closedType = openType.MakeGenericType(typeof (string), typeof (Int32));
                //构造封闭类型的实例
                var o = Activator.CreateInstance(closedType);
                Console.WriteLine(o.GetType());
    
                Console.ReadKey();
            }

     三、设置支持加载项的引用程序

    1,HostSDK.dll接口设计

        public interface IAddIn
        {
            string DoSomething(int x);
        }

    2,加载项AddInTypes.dll设计

        public class AddIn_A : IAddIn
        {
            public string DoSomething(int x)
            {
                return "AddIn_A:" + x;
            }
        }
        public class AddIn_B : IAddIn
        {
            public string DoSomething(int x)
            {
                return "AddIn_B:" + x;
            }
        }

    3,可扩展应用程序设计

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    using System.Threading.Tasks;
    using HostSDK;
    
    namespace Host
    {
        class Program
        {
            static void Main(string[] args)
            {
                //查找宿主EXE文件所在的目录(例如:F:/../../bin/Debug)
                string AddInDir = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
    
                //假定加载项程序集和宿主EXE文件在一个目录(例如:F:/../../bin/Debug/HostSDK.dll)
                var AddInAssemblies = Directory.EnumerateFiles(AddInDir, "*.dll");
               
                var AddInTypes = from file in AddInAssemblies
                                 let assembly = Assembly.LoadFrom(file)
                    from t in assembly.ExportedTypes//公开到处的类型
                    //如果类型实现了IAddIn接口,该接口就可由宿主使用
                    where t.IsClass && typeof (IAddIn).GetTypeInfo().IsAssignableFrom(t.GetTypeInfo())
                    select t;
                foreach (var addInType in AddInTypes)
                {
                    IAddIn ai = (IAddIn) Activator.CreateInstance(addInType);
                    Console.WriteLine(ai.DoSomething(5));
                }
    
                Console.ReadKey();
            }
        }
    }

    四、使用反射发现类型的成员

     1,发现类型的成员

            static void Main(string[] args)
            {
              
                //遍历这个AppDomain中加载的所有程序集
                Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
    
                foreach (Assembly a in assemblies)
                {
                    //查找程序集中的类型
                    foreach (Type t in a.ExportedTypes)
                    {
                        //发现类型的成员
                        foreach (MemberInfo mi in t.GetTypeInfo().DeclaredMembers)
                        {
                            if (mi is Type)//嵌套类型
                            if (mi is FieldInfo)//字段
                            if (mi is MethodInfo)//方法
                            if (mi is ConstructorInfo)//构造函数
                            if (mi is PropertyInfo)//属性
                            if (mi is EventInfo)//事件
                        }
                    }
                }
            }

     MemberInfo的所有派生类型都通用的属性和方法

    成员名称

    成员类型

    说明

    Name

    一个String属性

    返回成员名称

    DeclaringType

    一个Type属性

    返回声明成员的Type

    Module

    一个Module属性

    返回声明成员的Module

    CustomAttributes

    该属性返回一个IEnumerable<CustomAttributeData>

    返回一个集合,其中每个元素都标识了应用于该成员的一个定制特性的实例。定制特性可应用于如何成员。虽然Assembly不从MemberInfo派生,但它提供了可用于程序集的相同属性

    2,调用类型成员

    成员类型

    调用(Invoke)成员而需要调用(call)的方法

    FieldInfo

    调用GetValue获取字段的值

    调用SetValue设置字段的值

    ConstructorInfo

    调用Invoke构造类型的实例并调用构造器

    MethodInfo

    调用Invoke来调用类型的方法

    PropertyInfo

    调用GetValue来调用的属性的get访问器方法

    调用SetValue来调用属性的set访问器方法

    EventInfo

    调用AddEventHandler来调用事件的add访问器方法

    调用RemoveEventHandler来调用事件的remove访问器方法

    BindToMemberThenInvokeTheMember:方法演示了如何绑定到成员并调用它

    BindToMemberCreateDelegateToMemberThenInvokeThenMember:方法演示了如何绑定到一个对象或成员,然后创建一个委托来引用对象或成员。通过委托来调用的速度很快。如果需要在相容的对象上多次调用相同的成员,这个技术的性能比上一个好

    UseDynamicToBindAndInvokeTheMember:方法演示了如何利用C#的dynamic基元类型简化成员访问语法。此外,在相同类型的不同对象上调用相同成员时,这个技术还能提供不错的性能,因为针对每个类型,绑定都只会发生一次。而且可以缓存起来,以后多次调用的速度回非常快。用这个技术也可以调用不容类型的对象的成员

        internal sealed class SomeType
        {
            private Int32 m_someField;
            public SomeType(ref Int32 x){x *= 2;}
            public override string ToString(){return m_someField.ToString();}
            public Int32 SomeProp
            {
                get { return m_someField;;}
                set
                {
                    if(value<1)throw new ArgumentOutOfRangeException("value");
                    m_someField = value;
                }
    
            }
            public event EventHandler SomeEvent;
            public void NoCompilerWarnings(){SomeEvent.ToString();}
        }
        internal static class ReflectionExtensions
        {
            //这个辅助扩展方法简化了创建委托的语法
            public static TDelegate CreateDelegate<TDelegate>(this MethodInfo mi, object target = null)
            {
                return (TDelegate) (object) mi.CreateDelegate(typeof (TDelegate), target);
            }
        }
     public class Program
        {
            static void Main(string[] args)
            {
                Type t = typeof (SomeType);
                BindToMemberThenInvokeTheMember(t);
                BindToMemberCreateDelegateToMemberThenInvokeThenMember(t);
                UseDynamicToBindAndInvokeTheMember(t);
                Console.ReadKey();
            }
    
            private static void BindToMemberThenInvokeTheMember(Type t)
            {
                //构造实例
                Type ctorArgument = Type.GetType("System.Int32&");//或者typeof (Int32).MakeByRefType();
                ConstructorInfo ctor= t.GetTypeInfo().DeclaredConstructors.First(c => c.GetParameters()[0].ParameterType == ctorArgument);
                object[] args = new object[] {12};//构造器实参
                object obj = ctor.Invoke(args);
    
                //读写字段
                FieldInfo fi = obj.GetType().GetTypeInfo().GetDeclaredField("m_someField");
                fi.SetValue(obj, 33);
                Console.WriteLine(fi.GetValue(obj));
    
                //调用方法
                MethodInfo mi= obj.GetType().GetTypeInfo().GetDeclaredMethod("ToString");
                string s = (string) mi.Invoke(obj,null);
                Console.WriteLine(s);
    
                //读写属性
                PropertyInfo pi= obj.GetType().GetTypeInfo().GetDeclaredProperty("SomeProp");
                try
                {
                    pi.SetValue(obj,0,null);
                }
                catch (TargetInvocationException e)
                {
                    if (e.InnerException.GetType() != typeof (ArgumentOutOfRangeException)) throw;
                }
                pi.SetValue(obj,2);
                Console.WriteLine(pi.GetValue(obj));
    
                //为事件添加和删除方法
                EventInfo ei = obj.GetType().GetTypeInfo().GetDeclaredEvent("SomeEvent");
                EventHandler eh = new EventHandler(EventCallback);
                ei.AddEventHandler(obj, eh);
                ei.RemoveEventHandler(obj, eh);
    
            }
    
            private static void BindToMemberCreateDelegateToMemberThenInvokeThenMember(Type t)
            {
                //构造实例(不能创建对构造器的委托)
                object[] args = new object[] {12};
                object obj = Activator.CreateInstance(t, args);
                //注意不能创建对字段的委托
    
                //调用方法
                MethodInfo mi= obj.GetType().GetTypeInfo().GetDeclaredMethod("ToString");
                var toString = mi.CreateDelegate<Func<string>>(obj);
    
                //读写属性
                PropertyInfo pi = obj.GetType().GetTypeInfo().GetDeclaredProperty("SomeProp");
                var setSomeProp = pi.SetMethod.CreateDelegate<Action<Int32>>(obj);
                setSomeProp(2);
                var getSomeProp = pi.GetMethod.CreateDelegate<Func<Int32>>(obj);
                Console.WriteLine(getSomeProp());
    
                //向事件增删委托
                EventInfo ei = obj.GetType().GetTypeInfo().GetDeclaredEvent("SomeEvent");
                var addSomeEvent= ei.AddMethod.CreateDelegate<Action<EventHandler>>(obj);
                addSomeEvent(EventCallback);
                var removeSomeEvent = ei.RemoveMethod.CreateDelegate<Action<EventHandler>>(obj);
                removeSomeEvent(EventCallback);
            }
    
            private static void UseDynamicToBindAndInvokeTheMember(Type t)
            {
                //构造实例(不能创建对构造器的委托)
                object[] args = new object[] {12};
                dynamic obj = Activator.CreateInstance(t, args);
    
                try
                {
                    //读写字段
                    obj.m_someField = 5;
                    Int32 v = (Int32) obj.m_someField;
                }
                catch (RuntimeBinderException e)
                {
                    //之所以会执行到这里,是因为字段是私有的
                }
    
                //调用方法
                String s = obj.ToString();
                Console.WriteLine(s);
    
                //读写属性
                obj.SomeProp = 2;
                Console.WriteLine(obj.SomeProp);
    
                //从事件增删委托
                obj.SomeEvent += new EventHandler(EventCallback);
                obj.SomeEvent -= new EventHandler(EventCallback);
            }
    
            //添加到事件回调方法
            private static void EventCallback(object sender, EventArgs e){}
        }

    成员类型调用(Invoke)成员而需要调用(call)的方法FieldInfo调用GetValue获取字段的值调用SetValue设置字段的值ConstructorInfo调用Invoke构造类型的实例并调用构造器MethodInfo调用Invoke来调用类型的方法PropertyInfo调用GetValue来调用的属性的get访问器方法调用SetValue来调用属性的set访问器方法EventInfo调用AddEventHandler来调用事件的add访问器方法调用RemoveEventHandler来调用事件的remove访问器方法

    3,使用绑定句柄减少进程的内存消耗

    许多应用程序都绑定了一组类型(Type对象)或者类型成员(MemberInfo派生对象),并将这些类型对象保存在某种形式的集合中。Type和MemberInfo派生会对象需要大量内存。

    FCL定义了RuntimeTypeHandle、RuntimeFieldHandle、RuntimeMethodHandle三个运行时句柄类型,这些类型都只包含一个IntPtr,这使类型的实例显得相当精简(相当省内存)

    ①将Type装换为RuntimeTypeHandle:调用Type的静态GetTypeHandle方法并传递那个Type对象引用

    ②将RuntimeTypeHandle转换为Type对象:调用Type的静态方法GetTypeFromHandle,并传递那个RuntimeTypeHandle

    ③将FieldInfo对象转换为一个RuntimeFieldHandle:查询FieldInfo的实例只读属性FieldHandle

    ④将RuntimeFieldHandle转换为FieldInfo对象:调用FieldInfo的静态方法GetFieldFromHandle

    ⑤将MethodInfo对象转换为一个RuntimeMethodHandle:查询MethodInfo的实例只读属性MethodHandle

    ⑥将RuntimeMethodHandle转换MethodInfo对象:调用MethodInfo的静态方法GetMethodFromHandle

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace ConsoleApplication1
    {
        class Program
        {
            private const BindingFlags c_bf =
                BindingFlags.FlattenHierarchy | 
                BindingFlags.Instance | 
                BindingFlags.Static | 
                BindingFlags.Public |
                BindingFlags.NonPublic;
    
            static void Main(string[] args)
            {
                
                Show("显示在任何反射操作之前堆的大小");
    
                //为 MSCorlib.dll中的所有方法构建MethodInfo对象缓存
                List<MethodBase> methodInfos = new List<MethodBase>();
                foreach (Type t in typeof(Object).Assembly.GetExportedTypes())
                {
                    //跳过任何泛型类型
                    if(t.IsGenericTypeDefinition)continue;
    
                    MethodBase[] bm = t.GetMethods(c_bf);
                    methodInfos.AddRange(bm);
                }
    
                //显示当绑定所有方法之后,方法的个数和堆的大小
                Console.WriteLine("methodInfos集合数量={0:N0}", methodInfos.Count);
                Show("构建methodInfos之后");
    
                //为所有MethodInfo对象构建RuntimeMethodHandle缓存
                List<RuntimeMethodHandle> methodHandles = methodInfos.ConvertAll<RuntimeMethodHandle>(mb => mb.MethodHandle);
                Show("构建methodHandles之后");
    
                GC.KeepAlive(methodInfos); //阻止缓存被过早的垃圾回收
                methodInfos = null; //现在允许缓存垃圾回收
                Show("构建methodHandles之后,并且methodInfos = null");
    
                methodInfos = methodHandles.ConvertAll<MethodBase>(rmh => MethodInfo.GetMethodFromHandle(rmh));
                Show("重新构建methodInfos之后");
    
                GC.KeepAlive(methodHandles);//阻止缓存被过早的垃圾回收
                GC.KeepAlive(methodInfos);//阻止缓存被过早的垃圾回收
    
                methodHandles = null;//现在允许缓存垃圾回收
                methodInfos = null; //现在允许缓存垃圾回收
    
                Show("最后");
    
                Console.ReadKey();
            }
    
            private static void Show(string s)
            {
                //GC.GetTotalMemory(true)返回之前等待垃圾回收
                Console.WriteLine(" Heap size={0,12:N0} - {1}",GC.GetTotalMemory(true),s);
            }
        }
    }

  • 相关阅读:
    hdu2222 AC自动机入门
    bzoj1095: [ZJOI2007]Hide 捉迷藏 动态点分治学习
    【NOI2014】起床困难综合症 贪心
    bzoj1822: [JSOI2010]Frozen Nova 冷冻波网络流
    洛谷3767 膜法 带权并查集+分治
    NOI2015品酒大会 后缀数组
    NOI2015程序自动分析 并查集
    NOI2015软件包管理器 树剖线段树
    51nod1244 欧拉函数之和 杜教筛
    51nod1244 莫比乌斯函数之和 杜教筛
  • 原文地址:https://www.cnblogs.com/zd1994/p/7207825.html
Copyright © 2011-2022 走看看