您的位置:首页 > 编程语言 > C#

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

2008-09-07 12:38 423 查看
反射(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 Libraries,DLL)能够被构造以便从基础协议中分离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()调用,索引器的get和set访问器可以像特性的get和set访问器一样的获得。
Reflected类是通过使用Activator.CreateInstance()方法实例化的。然后,实例化的对象就作为MethodInfo对象的Invoke()方法的第一个参数。这就标识了哪个对象要调用该方法。Invoke()方法的第二个参数是要发送到方法的参数列表,如果有参数它就会是对象的数组。在这种情况下,没有参数发送到方法,因此Invoke()方法的第二个参数设置为null。
接下来两行展示了怎样动态的调用实例方法。该语法与特性的get访问器的说明一样。然而,用于特性的中间步骤是不必要的,并且用Type对象的GetMethod()方法能够直接获得方法。
Reflection.Emit的API提供了动态地创建新的配件的手段。使用自定义的生成器(builder)并生成Microsoft的中间语言(Microsoft Intermediate Language,MSIL)或者通用中间语言(Common Intermediate Language,CIL)代码,可使程序在运行时创建新的程序。这些配件可以被动态地调用或者保存到文件,在那里它们可以被重新加载和调用,或者被其他程序使用。
对于编译器或脚本引擎的后端工具,例如Web浏览器,动态配件的创建是非常有用的。使用Reflection.Emit的API,任何工具都可以扩展为动态的支持.NET或任何其它的通用语言基础结构(Common Language Infrastructure,CLI)适用系统。下面的程序展示了怎样生成动态配件,并把它保存为控制台程序。
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");
}这部分内容还是不太明白。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: