程序集加载和反射

一、程序集加载

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);
        }
    }
}

原文地址:https://www.cnblogs.com/zd1994/p/7207825.html