Code Listings
Chapter 17: Reflection and Metadata
Dynamically instantiating a delegate:
class Program { delegate int IntFunc (int x); static int Square (int x) { return x * x; } // Static method int Cube (int x) { return x * x * x; } // Instance method static void Main() { Delegate staticD = Delegate.CreateDelegate (typeof (IntFunc), typeof (Program), "Square"); Delegate instanceD = Delegate.CreateDelegate (typeof (IntFunc), new Program(), "Cube"); Console.WriteLine (staticD.DynamicInvoke (3)); // 9 Console.WriteLine (instanceD.DynamicInvoke (3)); // 27 } }
IntFunc f = (IntFunc) staticD; Console.WriteLine (f(3)); // 9 (but much faster!)
DeclaringType vs. ReflectedType:
class Program { static void Main() { // MethodInfo is a subclass of MemberInfo; see Figure 17-1. MethodInfo test = typeof (Program).GetMethod ("ToString"); MethodInfo obj = typeof (object) .GetMethod ("ToString"); Console.WriteLine (test.DeclaringType); // System.Object Console.WriteLine (obj.DeclaringType); // System.Object Console.WriteLine (test.ReflectedType); // Program Console.WriteLine (obj.ReflectedType); // System.Object Console.WriteLine (test == obj); // False } }
Console.WriteLine (test.MethodHandle == obj.MethodHandle); // True Console.WriteLine (test.MetadataToken == obj.MetadataToken // True && test.Module == obj.Module);
Generic type members:
PropertyInfo open = typeof (IEnumerator<>) .GetProperty ("Current"); PropertyInfo closed = typeof (IEnumerator<int>).GetProperty ("Current"); Console.WriteLine (open); // T Current Console.WriteLine (closed); // Int32 Current Console.WriteLine (open .PropertyType.IsGenericParameter); // True Console.WriteLine (closed.PropertyType.IsGenericParameter); // False
PropertyInfo open = typeof (List<>) .GetProperty ("Count"); PropertyInfo closed = typeof (List<int>).GetProperty ("Count"); Console.WriteLine (open); // Int32 Count Console.WriteLine (closed); // Int32 Count Console.WriteLine (open == closed); // False Console.WriteLine (open .DeclaringType.IsGenericTypeDefinition); // True Console.WriteLine (closed.DeclaringType.IsGenericTypeDefinition); // False
Method parameters:
Type type = typeof (string); Type[] parameterTypes = { typeof (int) }; MethodInfo method = type.GetMethod ("Substring", parameterTypes); object[] arguments = { 2 }; object returnValue = method.Invoke ("stamp", arguments); Console.WriteLine (returnValue); // "amp"
ParameterInfo[] paramList = method.GetParameters(); foreach (ParameterInfo x in paramList) { Console.WriteLine (x.Name); // startIndex Console.WriteLine (x.ParameterType); // System.Int32 }
Passing by reference:
object[] args = { "23", 0 }; Type[] argTypes = { typeof (string), typeof (int).MakeByRefType() }; MethodInfo tryParse = typeof (int).GetMethod ("TryParse", argTypes); bool successfulParse = (bool) tryParse.Invoke (null, args); Console.WriteLine (successfulParse + " " + args[1]); // True 23
Using delegates for performance:
delegate string StringToString (string s); static void Main() { MethodInfo trimMethod = typeof (string).GetMethod ("Trim", new Type[0]); var trim = (StringToString) Delegate.CreateDelegate (typeof (StringToString), trimMethod); for (int i = 0; i < 1000000; i++) trim ("test"); }
Accessing nonpublic members:
class Walnut { private bool cracked; public void Crack() { cracked = true; } public override string ToString() { return cracked.ToString(); } }
Type t = typeof (Walnut); Walnut w = new Walnut(); w.Crack(); FieldInfo f = t.GetField ("cracked", BindingFlags.NonPublic | BindingFlags.Instance); f.SetValue (w, false); Console.WriteLine (w); // False
Generic methods:
class Program { public static T Echo<T> (T x) { return x; } static void Main() { MethodInfo echo = typeof (Program).GetMethod ("Echo"); Console.WriteLine (echo.IsGenericMethodDefinition); // True echo.Invoke (null, new object[] { 123 } ); // Exception } }
MethodInfo echo = typeof (Program).GetMethod ("Echo"); MethodInfo intEcho = echo.MakeGenericMethod (typeof (int)); Console.WriteLine (intEcho.IsGenericMethodDefinition); // False Console.WriteLine (intEcho.Invoke (null, new object[] { 3 } )); // 3
Anonymously calling members of a generic type:
public static string ToStringEx (object value) { if (value == null) return "<null>"; if (value.GetType().IsPrimitive) return value.ToString(); StringBuilder sb = new StringBuilder(); if (value is IList) sb.Append ("List of " + ((IList)value).Count + " items: "); Type closedIGrouping = value.GetType().GetInterfaces() .Where (t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof (IGrouping<,>)) .FirstOrDefault(); if (closedIGrouping != null) // Call the Key property on IGrouping<,> { PropertyInfo pi = closedIGrouping.GetProperty ("Key"); object key = pi.GetValue (value, null); sb.Append ("Group with key=" + key + ": "); } if (value is IEnumerable) foreach (object element in ((IEnumerable)value)) sb.Append (ToStringEx (element) + " "); if (sb.Length == 0) sb.Append (value.ToString()); return "\r\n" + sb.ToString(); }
Defining your own attribute:
[AttributeUsage (AttributeTargets.Method)] public sealed class TestAttribute : Attribute { public int Repetitions; public string FailureMessage; public TestAttribute () : this (1) {} public TestAttribute (int repetitions) { Repetitions = repetitions; } }
Applying the custom attribute:
class Foo { [Test] public void Method1() { ... } [Test(20)] public void Method2() { ... } [Test(20, FailureMessage="Debugging Time!")] public void Method3() { ... } }
Reflecting over the custom attribute:
foreach (MethodInfo mi in typeof (Foo).GetMethods()) { TestAttribute att = (TestAttribute) Attribute.GetCustomAttribute (mi, typeof (TestAttribute)); if (att != null) Console.WriteLine ("Method {0} will be tested; reps={1}; msg={2}", mi.Name, att.Repetitions, att.FailureMessage); }
foreach (MethodInfo mi in typeof (Foo).GetMethods()) { TestAttribute att = (TestAttribute) Attribute.GetCustomAttribute (mi, typeof (TestAttribute)); if (att != null) for (int i = 0; i < att.Repetitions; i++) try { mi.Invoke (new Foo(), null); // Call method with no arguments } catch (Exception ex) // Wrap exception in att.FailureMessage { throw new Exception ("Error: " + att.FailureMessage, ex); } }
[Serializable, Obsolete] class Test { static void Main() { object[] atts = Attribute.GetCustomAttributes (typeof (Test)); foreach (object att in atts) Console.WriteLine (att); } }
Retrieving attributes in the reflection-only context:
IList<CustomAttributeData> atts = CustomAttributeData.GetCustomAttributes (myReflectionOnlyType); foreach (CustomAttributeData att in atts) { Console.Write (att.GetType()); // Attribute type Console.WriteLine (" " + att.Constructor); // ConstructorInfo object foreach (CustomAttributeTypedArgument arg in att.ConstructorArguments) Console.WriteLine (" " +arg.ArgumentType + "=" + arg.Value); foreach (CustomAttributeNamedArgument arg in att.NamedArguments) Console.WriteLine (" " + arg.MemberInfo.Name + "=" + arg.TypedValue); }
Adding an assembly resolution event handler:
ResolveEventHandler handler = (object sender, ResolveEventArgs args) => Assembly.ReflectionOnlyLoad (args.Name); AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += handler; // Reflect over attributes... AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= handler;
Dynamic code generation with DynamicMethod:
public class Test { static void Main() { var dynMeth = new DynamicMethod ("Foo", null, null, typeof (Test)); ILGenerator gen = dynMeth.GetILGenerator(); gen.EmitWriteLine ("Hello world"); gen.Emit (OpCodes.Ret); dynMeth.Invoke (null, null); // Hello world } }
Accessing private members of a type:
public class Test { static void Main() { var dynMeth = new DynamicMethod ("Foo", null, null, typeof (Test)); ILGenerator gen = dynMeth.GetILGenerator(); MethodInfo privateMethod = typeof(Test).GetMethod ("HelloWorld", BindingFlags.Static | BindingFlags.NonPublic); gen.Emit (OpCodes.Call, privateMethod); // Call HelloWorld gen.Emit (OpCodes.Ret); dynMeth.Invoke (null, null); // Hello world } static void HelloWorld() // private method, yet we can call it { Console.WriteLine ("Hello world"); } }
The evaluation stack:
var dynMeth = new DynamicMethod ("Foo", null, null, typeof(void)); ILGenerator gen = dynMeth.GetILGenerator(); MethodInfo writeLineInt = typeof (Console).GetMethod ("WriteLine", new Type[] { typeof (int) }); // The Ldc* op-codes load numeric literals of various types and sizes. gen.Emit (OpCodes.Ldc_I4, 123); // Push a 4-byte integer onto stack gen.Emit (OpCodes.Call, writeLineInt); gen.Emit (OpCodes.Ret); dynMeth.Invoke (null, null); // 123
Adding 2 + 2:
gen.Emit (OpCodes.Ldc_I4, 2); // Push a 4-byte integer, value=2 gen.Emit (OpCodes.Ldc_I4, 2); // Push a 4-byte integer, value=2 gen.Emit (OpCodes.Add); // Add the result together gen.Emit (OpCodes.Call, writeLineInt);
Calculating 10 / 2 + 1, option A:
gen.Emit (OpCodes.Ldc_I4, 10); gen.Emit (OpCodes.Ldc_I4, 2); gen.Emit (OpCodes.Div); gen.Emit (OpCodes.Ldc_I4, 1); gen.Emit (OpCodes.Add); gen.Emit (OpCodes.Call, writeLineInt);
Calculating 10 / 2 + 1, option B:
gen.Emit (OpCodes.Ldc_I4, 1); gen.Emit (OpCodes.Ldc_I4, 10); gen.Emit (OpCodes.Ldc_I4, 2); gen.Emit (OpCodes.Div); gen.Emit (OpCodes.Add); gen.Emit (OpCodes.Call, writeLineInt);
Passing arguments to a DynamicMethod:
DynamicMethod dynMeth = new DynamicMethod ("Foo", typeof (int), // Return type = int new[] { typeof (int), typeof (int) }, // Parameter types = int, int typeof (void)); ILGenerator gen = dynMeth.GetILGenerator(); gen.Emit (OpCodes.Ldarg_0); // Push first arg onto eval stack gen.Emit (OpCodes.Ldarg_1); // Push second arg onto eval stack gen.Emit (OpCodes.Add); // Add them together (result on stack) gen.Emit (OpCodes.Ret); // Return with stack having 1 value int result = (int) dynMeth.Invoke (null, new object[] { 3, 4 } ); // 7
Generating local variables:
var dynMeth = new DynamicMethod ("Test", null, null, typeof (void)); ILGenerator gen = dynMeth.GetILGenerator(); LocalBuilder localX = gen.DeclareLocal (typeof (int)); // Declare x LocalBuilder localY = gen.DeclareLocal (typeof (int)); // Declare y gen.Emit (OpCodes.Ldc_I4, 6); // Push literal 6 onto eval stack gen.Emit (OpCodes.Stloc, localX); // Store in localX gen.Emit (OpCodes.Ldc_I4, 7); // Push literal 7 onto eval stack gen.Emit (OpCodes.Stloc, localY); // Store in localY gen.Emit (OpCodes.Ldloc, localX); // Push localX onto eval stack gen.Emit (OpCodes.Ldloc, localY); // Push localY onto eval stack gen.Emit (OpCodes.Mul); // Multiply values together gen.Emit (OpCodes.Stloc, localX); // Store the result to localX gen.EmitWriteLine (localX); // Write the value of localX gen.Emit (OpCodes.Ret); dynMeth.Invoke (null, null); // 42
Branching:
ILGenerator gen = ... Label startLoop = gen.DefineLabel(); // Declare labels Label endLoop = gen.DefineLabel(); LocalBuilder x = gen.DeclareLocal (typeof (int)); // int x gen.Emit (OpCodes.Ldc_I4, 5); // gen.Emit (OpCodes.Stloc, x); // x = 5 gen.MarkLabel (startLoop); gen.Emit (OpCodes.Ldc_I4, 10); // Load 10 onto eval stack gen.Emit (OpCodes.Ldloc, x); // Load x onto eval stack gen.Emit (OpCodes.Blt, endLoop); // if (x > 10) goto endLoop gen.EmitWriteLine (x); // Console.WriteLine (x) gen.Emit (OpCodes.Ldloc, x); // Load x onto eval stack gen.Emit (OpCodes.Ldc_I4, 1); // Load 1 onto the stack gen.Emit (OpCodes.Add); // Add them together gen.Emit (OpCodes.Stloc, x); // Save result back to x gen.Emit (OpCodes.Br, startLoop); // return to start of loop gen.MarkLabel (endLoop); gen.Emit (OpCodes.Ret);
Instanting objects and calling instance methods:
var dynMeth = new DynamicMethod ("Test", null, null, typeof (void)); ILGenerator gen = dynMeth.GetILGenerator(); ConstructorInfo ci = typeof (StringBuilder).GetConstructor (new Type[0]); gen.Emit (OpCodes.Newobj, ci); gen.Emit (OpCodes.Callvirt, typeof (StringBuilder) .GetProperty ("MaxCapacity").GetGetMethod()); gen.Emit (OpCodes.Call, typeof (Console).GetMethod ("WriteLine", new[] { typeof (int) } )); gen.Emit (OpCodes.Ret); dynMeth.Invoke (null, null); // 2147483647
Constructing and using a StringBuilder:
// We will call: new StringBuilder ("Hello", 1000) ConstructorInfo ci = typeof (StringBuilder).GetConstructor ( new[] { typeof (string), typeof (int) } ); gen.Emit (OpCodes.Ldstr, "Hello"); // Load a string onto the eval stack gen.Emit (OpCodes.Ldc_I4, 1000); // Load an int onto the eval stack gen.Emit (OpCodes.Newobj, ci); // Construct the StringBuilder Type[] strT = { typeof (string) }; gen.Emit (OpCodes.Ldstr, ", world!"); gen.Emit (OpCodes.Call, typeof (StringBuilder).GetMethod ("Append", strT)); gen.Emit (OpCodes.Callvirt, typeof (object).GetMethod ("ToString")); gen.Emit (OpCodes.Call, typeof (Console).GetMethod ("WriteLine", strT)); gen.Emit (OpCodes.Ret); dynMeth.Invoke (null, null); // Hello, world!
Exception handling:
MethodInfo getMessageProp = typeof (NotSupportedException) .GetProperty ("Message").GetGetMethod(); MethodInfo writeLineString = typeof (Console).GetMethod ("WriteLine", new[] { typeof (object) } ); gen.BeginExceptionBlock(); ConstructorInfo ci = typeof (NotSupportedException).GetConstructor ( new Type[0] ); gen.Emit (OpCodes.Newobj, ci); gen.Emit (OpCodes.Throw); gen.BeginCatchBlock (typeof (NotSupportedException)); gen.Emit (OpCodes.Callvirt, getMessageProp); gen.Emit (OpCodes.Call, writeLineString); gen.BeginFinallyBlock(); gen.EmitWriteLine ("Finally"); gen.EndExceptionBlock();
Emitting assemblies and types:
AppDomain appDomain = AppDomain.CurrentDomain; AssemblyName aname = new AssemblyName ("MyDynamicAssembly"); AssemblyBuilder assemBuilder = appDomain.DefineDynamicAssembly (aname, AssemblyBuilderAccess.Run); ModuleBuilder modBuilder = assemBuilder.DefineDynamicModule ("DynModule"); TypeBuilder tb = modBuilder.DefineType ("Widget", TypeAttributes.Public); MethodBuilder methBuilder = tb.DefineMethod ("SayHello", MethodAttributes.Public, null, null); ILGenerator gen = methBuilder.GetILGenerator(); gen.EmitWriteLine ("Hello world"); gen.Emit (OpCodes.Ret); Type t = tb.CreateType(); object o = Activator.CreateInstance (t); t.GetMethod ("SayHello").Invoke (o, null); // Hello world
Saving emitted assemblies:
AppDomain domain = AppDomain.CurrentDomain; AssemblyName aname = new AssemblyName ("MyEmissions"); aname.Version = new Version (2, 13, 0, 1); AssemblyBuilder assemBuilder = domain.DefineDynamicAssembly ( aname, AssemblyBuilderAccess.RunAndSave); ModuleBuilder modBuilder = assemBuilder.DefineDynamicModule ( "MainModule", "MyEmissions.dll"); // Create types as we did previously... // ... assemBuilder.Save ("MyEmissions.dll");
Emitting type members: boilerplate declarations:
AppDomain domain = AppDomain.CurrentDomain; AssemblyName aname = new AssemblyName ("MyEmissions"); AssemblyBuilder assemBuilder = domain.DefineDynamicAssembly ( aname, AssemblyBuilderAccess.RunAndSave); ModuleBuilder modBuilder = assemBuilder.DefineDynamicModule ( "MainModule", "MyEmissions.dll"); TypeBuilder tb = modBuilder.DefineType ("Widget", TypeAttributes.Public);
Emitting methods:
MethodBuilder mb = tb.DefineMethod ("SquareRoot", MethodAttributes.Static | MethodAttributes.Public, CallingConventions.Standard, typeof (double), // Return type new[] { typeof (double) } ); // Parameter types mb.DefineParameter (1, ParameterAttributes.None, "value"); // Assign name ILGenerator gen = mb.GetILGenerator(); gen.Emit (OpCodes.Ldarg_0); // Load 1st arg gen.Emit (OpCodes.Call, typeof(Math).GetMethod ("Sqrt")); gen.Emit (OpCodes.Ret); Type realType = tb.CreateType(); double x = (double) tb.GetMethod ("SquareRoot").Invoke (null, new object[] { 10.0 }); Console.WriteLine (x); // 3.16227766016838
Passing by reference:
MethodBuilder mb = tb.DefineMethod ("SquareRoot", MethodAttributes.Static | MethodAttributes.Public, CallingConventions.Standard, null, new Type[] { typeof (double).MakeByRefType() } ); mb.DefineParameter (1, ParameterAttributes.None, "value"); ILGenerator gen = mb.GetILGenerator(); gen.Emit (OpCodes.Ldarg_0); gen.Emit (OpCodes.Ldarg_0); gen.Emit (OpCodes.Ldind_R8); gen.Emit (OpCodes.Call, typeof (Math).GetMethod ("Sqrt")); gen.Emit (OpCodes.Stind_R8); gen.Emit (OpCodes.Ret); Type realType = tb.CreateType(); object[] args = { 10.0 }; tb.GetMethod ("SquareRoot").Invoke (null, args); Console.WriteLine (args[0]); // 3.16227766016838
Emitting fields and properties:
FieldBuilder field = tb.DefineField ("_text", typeof (string), FieldAttributes.Private); PropertyBuilder prop = tb.DefineProperty ( "Text", // Name of property PropertyAttributes.None, typeof (string), // Property type new Type[0]); // Indexer types MethodBuilder getter = tb.DefineMethod ( "get_Text", // Method name MethodAttributes.Public | MethodAttributes.SpecialName, typeof (string), // Return type new Type[0]); // Parameter types ILGenerator getGen = getter.GetILGenerator(); getGen.Emit (OpCodes.Ldarg_0); // Load "this" onto eval stack getGen.Emit (OpCodes.Ldfld, field); // Load field value onto eval stack getGen.Emit (OpCodes.Ret); // Return MethodBuilder setter = tb.DefineMethod ( "set_Text", MethodAttributes.Assembly | MethodAttributes.SpecialName, null, // Return type new Type[] { typeof (string) } ); // Parameter types ILGenerator setGen = setter.GetILGenerator(); setGen.Emit (OpCodes.Ldarg_0); // Load "this" onto eval stack setGen.Emit (OpCodes.Ldarg_1); // Load 2nd arg, i.e., value setGen.Emit (OpCodes.Stfld, field); // Store value into field setGen.Emit (OpCodes.Ret); // return prop.SetGetMethod (getter); // Link the get method and property prop.SetSetMethod (setter); // Link the set method and property
Type t = tb.CreateType(); object o = Activator.CreateInstance (t); t.GetProperty ("Text").SetValue (o, "Good emissions!", new object[0]); string text = (string) t.GetProperty ("Text").GetValue (o, null); Console.WriteLine (text); // Good emissions!
Emitting constructors:
FieldBuilder field = tb.DefineField ("_capacity", typeof (int), FieldAttributes.Private); ConstructorBuilder c = tb.DefineConstructor ( MethodAttributes.Public, CallingConventions.Standard, new Type[0]); // Constructor parameters ILGenerator gen = c.GetILGenerator(); gen.Emit (OpCodes.Ldarg_0); // Load "this" onto eval stack gen.Emit (OpCodes.Ldc_I4, 4000); // Load 4000 onto eval stack gen.Emit (OpCodes.Stfld, field); // Store it to our field gen.Emit (OpCodes.Ret);
Calling base constructors:
gen.Emit (OpCodes.Ldarg_0); ConstructorInfo baseConstr = typeof (B).GetConstructor (new Type[0]); gen.Emit (OpCodes.Call, baseConstr);
Attaching attributes:
Type attType = typeof (XmlElementAttribute); ConstructorInfo attConstructor = attType.GetConstructor ( new Type[] { typeof (string) } ); var att = new CustomAttributeBuilder ( attConstructor, // Constructor new object[] { "FirstName" }, // Constructor arguments new PropertyInfo[] { attType.GetProperty ("Namespace"), // Properties attType.GetProperty ("Order") }, new object[] { "FirstName", 3 } // Property values ); myFieldBuilder.SetCustomAttribute (att); // or propBuilder.SetCustomAttribute (att); // or typeBuilder.SetCustomAttribute (att); etc
Emitting generic types and methods: boilerplate declarations:
AppDomain domain = AppDomain.CurrentDomain; AssemblyName aname = new AssemblyName ("MyEmissions"); AssemblyBuilder assemBuilder = domain.DefineDynamicAssembly ( aname, AssemblyBuilderAccess.RunAndSave); ModuleBuilder modBuilder = assemBuilder.DefineDynamicModule ( "MainModule", "MyEmissions.dll");
Defining generic methods:
TypeBuilder tb = modBuilder.DefineType ("Widget", TypeAttributes.Public); MethodBuilder mb = tb.DefineMethod ("Echo", MethodAttributes.Public | MethodAttributes.Static); GenericTypeParameterBuilder[] genericParams = mb.DefineGenericParameters ("T"); mb.SetSignature (genericParams[0], // Return type null, null, genericParams, // Parameter types null, null); mb.DefineParameter (1, ParameterAttributes.None, "value"); // Optional ILGenerator gen = mb.GetILGenerator(); gen.Emit (OpCodes.Ldarg_1); gen.Emit (OpCodes.Ret);
Defining generic types:
TypeBuilder tb = modBuilder.DefineType ("Widget", TypeAttributes.Public); GenericTypeParameterBuilder[] genericParams = tb.DefineGenericParameters ("T"); tb.DefineField ("Value", genericParams[0], FieldAttributes.Public);
Awkward emission targets—uncreated closed generics:
TypeBuilder tb = modBuilder.DefineType ("Widget", TypeAttributes.Public); MethodBuilder mb = tb.DefineMethod ("Test", MethodAttributes.Public | MethodAttributes.Static); ILGenerator gen = mb.GetILGenerator(); Type variableType = typeof (List<>).MakeGenericType (tb); ConstructorInfo open = typeof (List<>).GetConstructor (new Type[0]); ConstructorInfo ci = TypeBuilder.GetConstructor (variableType, open); LocalBuilder listVar = gen.DeclareLocal (variableType); gen.Emit (OpCodes.Newobj, ci); gen.Emit (OpCodes.Stloc, listVar); gen.Emit (OpCodes.Ret);
Awkward emission targets—circular dependencies:
var publicAtt = FieldAttributes.Public; TypeBuilder aBuilder = modBuilder.DefineType ("A"); TypeBuilder bBuilder = modBuilder.DefineType ("B"); FieldBuilder bee = aBuilder.DefineField ("Bee", bBuilder, publicAtt); FieldBuilder aye = bBuilder.DefineField ("Aye", aBuilder, publicAtt); Type realA = aBuilder.CreateType(); Type realB = bBuilder.CreateType();
var pub = FieldAttributes.Public; TypeBuilder aBuilder = modBuilder.DefineType ("A"); TypeBuilder bBuilder = modBuilder.DefineType ("B"); aBuilder.DefineField ("Bee", typeof(S<>).MakeGenericType (bBuilder), pub); bBuilder.DefineField ("Aye", typeof(S<>).MakeGenericType (aBuilder), pub);
TypeBuilder[] uncreatedTypes = { aBuilder, bBuilder }; ResolveEventHandler handler = delegate (object o, ResolveEventArgs args) { var type = uncreatedTypes.FirstOrDefault (t => t.FullName == args.Name); return type == null ? null : type.CreateType().Assembly; }; AppDomain.CurrentDomain.TypeResolve += handler; Type realA = aBuilder.CreateType(); Type realB = bBuilder.CreateType(); AppDomain.CurrentDomain.TypeResolve -= handler;
Writing an IL disassembler:
using System; using System.Text; using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; using System.Xml; class Disassembler { public static string Disassemble (MethodBase method) { return new Disassembler (method).Dis (); } static Dictionary<short, OpCode> _opcodes = new Dictionary<short, OpCode> (); static Disassembler () { foreach (FieldInfo fi in typeof (OpCodes).GetFields (BindingFlags.Public | BindingFlags.Static)) if (typeof (OpCode).IsAssignableFrom (fi.FieldType)) { OpCode code = (OpCode)fi.GetValue (null); if (code.OpCodeType != OpCodeType.Nternal) _opcodes.Add (code.Value, code); } } StringBuilder _output; Module _module; byte[] _il; int _pos; Disassembler (MethodBase method) { _module = method.DeclaringType.Module; _il = method.GetMethodBody ().GetILAsByteArray (); } string Dis () { _output = new StringBuilder (); while (_pos < _il.Length) DisassembleNextInstruction (); return _output.ToString (); } void DisassembleNextInstruction () { int opStart = _pos; OpCode code = ReadOpCode (); string operand = ReadOperand (code); _output.AppendFormat ("IL_{0:X4}: {1,-12} {2}", opStart, code.Name, operand); _output.AppendLine (); } string ReadOperand (OpCode c) { int operandLength = c.OperandType == OperandType.InlineNone ? 0 : c.OperandType == OperandType.ShortInlineBrTarget || c.OperandType == OperandType.ShortInlineI || c.OperandType == OperandType.ShortInlineVar ? 1 : c.OperandType == OperandType.InlineVar ? 2 : c.OperandType == OperandType.InlineI8 || c.OperandType == OperandType.InlineR ? 8 : c.OperandType == OperandType.InlineSwitch ? 4 * (BitConverter.ToInt32 (_il, _pos) + 1) : 4; if (_pos + operandLength > _il.Length) throw new Exception ("Unexpected end of IL"); string result = FormatOperand (c, operandLength); if (result == null) { result = ""; for (int i = 0; i < operandLength; i++) result += _il[_pos + i].ToString ("X2") + " "; } _pos += operandLength; return result; } OpCode ReadOpCode () { byte byteCode = _il[_pos++]; if (_opcodes.ContainsKey (byteCode)) return _opcodes[byteCode]; if (_pos == _il.Length) throw new Exception ("Cannot find opcode " + byteCode); short shortCode = (short)(byteCode * 256 + _il[_pos++]); if (!_opcodes.ContainsKey (shortCode)) throw new Exception ("Cannot find opcode " + shortCode); return _opcodes[shortCode]; } string FormatOperand (OpCode c, int operandLength) { if (operandLength == 0) return ""; if (operandLength == 4) return Get4ByteOperand (c); else if (c.OperandType == OperandType.ShortInlineBrTarget) return GetShortRelativeTarget(); else if (c.OperandType == OperandType.InlineSwitch) return GetSwitchTarget (operandLength); else return null; } string Get4ByteOperand (OpCode c) { int intOp = BitConverter.ToInt32 (_il, _pos); switch (c.OperandType) { case OperandType.InlineTok: case OperandType.InlineMethod: case OperandType.InlineField: case OperandType.InlineType: MemberInfo mi; try { mi = _module.ResolveMember (intOp); } catch { return null; } if (mi == null) return null; if (mi.ReflectedType != null) return mi.ReflectedType.FullName + "." + mi.Name; else if (mi is Type) return ((Type)mi).FullName; else return mi.Name; case OperandType.InlineString: string s = _module.ResolveString (intOp); if (s != null) s = "\"" + s + "\""; return s; case OperandType.InlineBrTarget: return "IL_" + (_pos + intOp + 4).ToString ("X4"); default: return null; } } string GetShortRelativeTarget() { return "IL_" + (_pos + (sbyte)_il[_pos] + 1).ToString ("X4"); } string GetSwitchTarget (int operandLength) { int targetCount = BitConverter.ToInt32 (_il, _pos); string [] targets = new string [targetCount]; for (int i = 0; i < targetCount; i++) { int ilTarget = BitConverter.ToInt32 (_il, _pos + (i + 1) * 4); targets [i] = "IL_" + (_pos + ilTarget + operandLength).ToString ("X4"); } return "(" + string.Join (", ", targets) + ")"; } }
© 2007, O'Reilly Media, Inc. All rights reserved