读书笔记_C#技术内幕_第二十八章(反射)

来源:互联网 发布:怎样上传淘宝宝贝 编辑:程序博客网 时间:2024/04/30 07:30
 

反射(reflection)是审查程序的元数据并收集关于它的类型信息的能力。使用反射就可以学习关于程序的配件、模块、和内部程序元素的所有类型。

反射对于设计工具特别有用。它基于使用的基础类型的元数据派生的用户选择,支持自动建立代码。反射也对后联编的框架提供出色的支持,其中运行环境要求迅速作出选择需要的库或其他功能的决定。

 

发现程序信息:

反射主要用来发现程序的信息。C#的反射的API可以找出关于程序的所有可用的信息。要搜索的程序元素包括配件、模块、类型和类型成员。

class Reflecting

    {

        static void Main(string[] args)

        {

            Reflecting reflect = new Reflecting();

 

            Assembly myAssembly = Assembly.LoadFrom("ConsoleApplication1.exe");

 

            reflect.GetReflectionInfo(myAssembly);

        }

 

        void GetReflectionInfo(Assembly myAssembly)

        {

            Type[] typeArr = myAssembly.GetTypes();

            foreach (Type type in typeArr)

            {

                Console.WriteLine("/nType: {0}/n", type.FullName);

 

                ConstructorInfo[] MyConstructors = type.GetConstructors();

                foreach (ConstructorInfo constructor in MyConstructors)

                {

                    Console.WriteLine("/tConstructor: {0}", constructor.ToString());

                }

                Console.WriteLine();

 

                FieldInfo[] MyFileds = type.GetFields();

                foreach (FieldInfo field in MyFileds)

                {

                    Console.WriteLine("/tFiled: {0}", field.ToString());

                }

                Console.WriteLine();

 

                MethodInfo[] MyMythods = type.GetMethods();

                foreach (MethodInfo method in MyMythods)

                {

                    Console.WriteLine("/tMethod: {0}", method.ToString());

                }

                Console.WriteLine();

 

                PropertyInfo[] MyProperties = type.GetProperties();

                foreach (PropertyInfo property in MyProperties)

                {

                    Console.WriteLine("/tProperty: {0}", property.ToString());

                }

                Console.WriteLine();

 

                EventInfo[] MyEvents = type.GetEvents();

                foreach (EventInfo anEvent in MyEvents)

                {

                    Console.WriteLine("/tEvent: {0}", anEvent.ToString());

                }

                Console.WriteLine();

            }

        }

    }

 

    public class Reflected

    {

        public int MyField;

        protected ArrayList myArray;

 

        public Reflected()

        {

            myArray = new ArrayList();

            myArray.Add("Some ArrayList Entry");

        }

 

        public float MyProperty

        {

            get { return MyEvent(); }

        }

 

        public object this[int index]

        {

            get

            {

                if (index < myArray.Count)

                {

                    return myArray[index];

                }

                else

                {

                    return null;

                }

            }

            set

            {

                myArray.Add(value);

            }

        }

 

        public float MyInstanceMethod()

        {

            Console.WriteLine("Invoke Instance MyMethod");

 

            return 0.02f;

        }

 

        public static float MyStaticMethod()

        {

            Console.WriteLine("Invoke Static MyMethod");

 

            return 0.02f;

        }

 

        public delegate float MyDelegate();

 

        public event MyDelegate MyEvent = new MyDelegate(MyStaticMethod);

 

        public enum MyEnum { valOne, valTwo, valThree };

    }

在上面的程序中,类Reflected通过反射的所有类型的类成员使类可用。

Main方法中,通过调用Assembly类的静态LoadFrom方法获得一个assembly对象。在GetReflectionInfo方法内,Assembly对象——myAssembly调用他的GetType方法以获得在配件中可用的所有类型的数组。

 

Assembly类型有一个GetModules方法,该方法将获取配件内的模块的数组。从每个模块,可以使用GetTypes来获取要使用的数组类型。简而言之,上面的程序使用了Assembly类型的GetTypes方法来获取属于那个配件的所有模块的所有类型。

 

可以得到的类型包括:构造函数,字段,方法,特性(包括索引器),事件。但是这些类型必须都是public

 

动态地激活代码:

动态的代码激活是决定运行时刻执行什么代码。这个能力在许多情况下是有用的,其中需要后联编框架。

考虑简单对象访问协议(SOAP)规范,其中传输协议是独立的。虽然SOAP通常与HTTP协议一块使用,建立规范本身是为了在其它协议上实现,例如,简单消息传输协议(SMTP)。使用适当的接口,动态链接库(Dynamic Link LibrariesDLL)能够被构造以便从基础协议中分离SOAP的实现。另外,通过后联编的实现,具有打包到自己的DLL中的适当接口的新协议,也能够在任何时间添加到框架中,而不必重新编译代码。通过协助决定在运行时刻SOAP包中使用什么传输协议,反射的后联编能力能够启用这个方案。下面的例子展示了运行时刻中指定的配件中,怎样通过动态地激活代码来执行后联编操作。

class Reflecting

    {

        static void Main(string[] args)

        {

            Reflecting reflect = new Reflecting();

 

            Assembly myAssembly = Assembly.LoadFrom("ConsoleApplication1.exe");

 

            reflect.DynamicallyInvokeMembers(myAssembly);

        }

 

        void DynamicallyInvokeMembers(Assembly myAssembly)

        {

            Type classType = myAssembly.GetType("ConsoleApplication1.Reflected");

 

            PropertyInfo myProperty = classType.GetProperty("MyProperty");

 

            MethodInfo propGet = myProperty.GetGetMethod();

 

            object reflectedObject = Activator.CreateInstance(classType);

 

            propGet.Invoke(reflectedObject, null);

 

            MethodInfo myMethod = classType.GetMethod("MyInstanceMethod");

 

            myMethod.Invoke(reflectedObject, null);

        }

}

下面是它的输出:

Invoking Static MyMethod.

Invoking Instance MyMethod.

 

DynamicallyInvokeMembers()方法使用Assembly对象Reflected类获得了Type对象。然后,Type对象用于获得MyProperty特性。其次,通过调用PropertyInfo对象的GetGetMethod()方法来获得MethodInfo对象。GetGetMethod()检索特性的get方法的拷贝,这是为了进行反射,像方法一样地对待。

借助GetGetMethod()GetSetMethod()调用,索引器的getset访问器可以像特性的getset访问器一样的获得。

Reflected类是通过使用Activator.CreateInstance()方法实例化的。然后,实例化的对象就作为MethodInfo对象的Invoke()方法的第一个参数。这就标识了哪个对象要调用该方法。Invoke()方法的第二个参数是要发送到方法的参数列表,如果有参数它就会是对象的数组。在这种情况下,没有参数发送到方法,因此Invoke()方法的第二个参数设置为null

接下来两行展示了怎样动态的调用实例方法。该语法与特性的get访问器的说明一样。然而,用于特性的中间步骤是不必要的,并且用Type对象的GetMethod()方法能够直接获得方法。

Reflection.EmitAPI提供了动态地创建新的配件的手段。使用自定义的生成器(builder)并生成Microsoft的中间语言(Microsoft Intermediate LanguageMSIL)或者通用中间语言(Common Intermediate LanguageCIL)代码,可使程序在运行时创建新的程序。这些配件可以被动态地调用或者保存到文件,在那里它们可以被重新加载和调用,或者被其他程序使用。

对于编译器或脚本引擎的后端工具,例如Web浏览器,动态配件的创建是非常有用的。使用Reflection.EmitAPI,任何工具都可以扩展为动态的支持.NET或任何其它的通用语言基础结构(Common Language InfrastructureCLI)适用系统。下面的程序展示了怎样生成动态配件,并把它保存为控制台程序。

static void Main(string[] args)

        {

            AppDomain myAppDomain = AppDomain.CurrentDomain;

            AssemblyName myAssemblyName = new AssemblyName();

            myAssemblyName.Name = "DynamicAssembly";

 

            AssemblyBuilder myAssemblyBuilder = myAppDomain.DefineDynamicAssembly(myAssemblyName, AssemblyBuilderAccess.RunAndSave);

 

            ModuleBuilder myModuleBuilder = myAssemblyBuilder.DefineDynamicModule("DynamicModule", "emitter.netmodule");

 

            TypeBuilder myTypeBuilder = myModuleBuilder.DefineType("EmitTestClass");

 

            MethodBuilder myMethodBuilder = myTypeBuilder.DefineMethod("Main", MethodAttributes.Public | MethodAttributes.Static, null, null);

 

            ILGenerator myILGenerator = myMethodBuilder.GetILGenerator();

            myILGenerator.EmitWriteLine("/n/tI must emit, reflection is pretty cool!/n");

            myILGenerator.Emit(OpCodes.Ret);

 

            Type myType = myTypeBuilder.CreateType();

            object myObjectInstance = Activator.CreateInstance(myType);

 

            Console.WriteLine("/nDynamic Invocation:");

 

            MethodInfo myMethod = myType.GetMethod("Main");

            myMethod.Invoke(myObjectInstance, null);

 

            myAssemblyBuilder.SetEntryPoint(myMethod);

            myAssemblyBuilder.Save("emitter.exe");

        }

这部分内容还是不太明白。
原创粉丝点击