浏览 1566 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
作者 | 正文 | ||||||||||||||||||||||
发表时间:2009-09-23
最后修改:2009-09-23
System.Reflection.Emit的功能,可能会觉得这帖标题很怪——Builder系列跟对应的Info系列不是同根生么,前者继承对应的后者:
如果你试用(没错字,我就是说“试用”而不是“使用”)过
(最后一个不是Info系列的,所以用灰色表示了。前面的Assembly、Module和Type都算在“广义的Info系列”里) 那么通过SRE创建出一个类型之后,手上的Builder系列类型可以直接用于反射吗? 答案是:不行,Builder系列唯一的使命就是Emit,没有别的用途。 看个例子就明白:demo.cs using System; using System.Reflection; using System.Reflection.Emit; static class Demo { static void Main(string[] args) { var assemblyName = new AssemblyName("DemoAssembly"); var assemblyBuilder = AppDomain.CurrentDomain .DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); var moduleBuilder = assemblyBuilder.DefineDynamicModule( "DemoAssembly" ); var typeBuilder = moduleBuilder.DefineType( "TestType00", TypeAttributes.Public); var fieldBuilder = typeBuilder.DefineField( "x", typeof(int), FieldAttributes.Private); var testType = typeBuilder.CreateType(); // get a FieldInfo through normal reflection var fieldInfo = testType.GetField( "x", BindingFlags.NonPublic | BindingFlags.Instance); // create new instance of TestType00 var instance = Activator.CreateInstance(testType); // set the field x through normal reflection fieldInfo.SetValue(instance, 2); try { // try to get the field x through FieldBuilder // a FieldBuilder is a FieldInfo, but this won't work... Console.WriteLine(fieldBuilder.GetValue(instance)); } catch (Exception e) { Console.WriteLine(e); //System.NotSupportedException: The invoked member is not supported in a dynamic module. // at System.Reflection.Emit.FieldBuilder.GetValue(Object obj) // at Demo.Main(String[] args) in d:\demo.cs:line 29 } // get a FieldInfo from a FieldBuilder var fieldInfoFromResolved = moduleBuilder.ResolveField( moduleBuilder.GetFieldToken(fieldBuilder).Token); // WARNING: can't call resolve before calling CreateType, // because resolving a type will load the type; an unfinished // type cannot be loaded Console.WriteLine(fieldInfo == fieldInfoFromResolved); // true // get the field x through converted FieldInfo Console.WriteLine(fieldInfoFromResolved.GetValue(instance)); // 2 // set the field x through converted FieldInfo fieldInfoFromResolved.SetValue(instance, 5); // get the field x through normal reflection Console.WriteLine(fieldInfo.GetValue(instance)); // 5 } } 稍微解释一下这段代码: 开头的部分都是很常见的SRE boilerplate,先得到AssemblyBuilder,然后ModuleBuilder,然后TypeBuilder。接下来定义了一个域,得到一个FieldBuilder。如开头所说,FieldBuilder继承FieldInfo,那么它似乎也应该可以用于反射?马上try一下,却得到NotSupportedException。此路不通。 接下来的部分就有趣了。FieldBuilder虽然不能用于反射,但它仍然持有正确的metadata token;而从metadata就可以找出“真正”的FieldInfo。代码中第38的调用完成了这个工作。可以看到,这个方法得到的FieldInfo与通过正常反射得到的FieldInfo是同一个对象,自然就可以用于反射了。 先前跟老赵在twitter上的对话: @rednaxelafx 写道 @jeffz_cn TypeBuilder不能直接用于反射,FieldBuilder不能用于Get/SetValue,MethodBuilder不能直接用于Invoke……一切都是因为metadata token……
@jeffz_cn 写道 @rednaxelafx FieldBuilder可以用来Stfld的,我的Eazy里刚用过,呵呵。你可以去获取代码看看,https://eazy.svn.codeplex.com/svn 在TypeBuilderExtensions.cs的79行。
老赵误解了我想说的“不能用于反射”的意思。Builder系列类型当然可以用于在生成IL时使用,因为那就是它们的本职工作——Emit。生成的IL在执行的时候并不会使用Builder系列类型的实例通过反射去取值/设值/调用等,当然没问题。一旦想把Builder系列直接用于Emit之外的用途,就会遇到NotSupportedException。 上面提到的转换,再举几个例子: var type = moduleBuilder.ResolveType(moduleBuilder.GetTypeToken(typeBuilder).Token); var fieldInfo = moduleBuilder.ResolveField(moduleBuilder.GetFieldToken(fieldBuilder).Token); var methodInfo = moduleBuilder.ResolveMethod(moduleBuilder.GetMethodToken(methodBuilder).Token); Metadata是以module为单位组织的,token只在module内部有效。所以ResolveXXX系列的方法也都是在ModuleBuilder上。 这种通过token去获取用于反射的Info系列类型的方法,比通过正常的反射API更高效易用。所以如果是刚动态创建出类型,手上还有Builder系列类型的实例的引用,就不必绕圈通过正常反射获取Info系列类型的实例了。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|||||||||||||||||||||||
返回顶楼 | |||||||||||||||||||||||