Speed Up Your Reflection Processes
Emitting Lines of Code
Basically what the emitter has to do is emit an algorithm that reads roughly like the following pseudocode:
For each item in list
Write property1's name and property1's value
Write property2's name and property2's value
Next item
Of course, this is very easy with VB code, but if you did that you'd have to revert to pure reflection or write the lines of code yourself. The code in Listing 7 writes that code for you but in MSIL (Microsoft Intermediate Language, which is an implementation of the standard CIL (Common Intermediate Language). MSIL is like assembly language, so you have to do it in little itty bitty pieces. The lines of code are numbered for reference only to assist with the long listing.
Listing 7: Some code that iterates over each property and writes the lines of MSIL to display the property name and value.
1. ' get the properties and use them to build writeline statements 2. Dim properties() As PropertyInfo = GetType(T).GetProperties() 3. Dim generator As ILGenerator = methodBuilder.GetILGenerator() 4. Dim localT As LocalBuilder = generator.DeclareLocal(GetType(T)) ' loc 1 5. Dim localI As LocalBuilder = _ generator.DeclareLocal(GetType(IEnumerator(Of T))) ' loc 2 6. Dim localB As LocalBuilder = _ generator.DeclareLocal(GetType(Boolean)) 'loc 3 7. generator.Emit(OpCodes.Nop) 8. generator.Emit(OpCodes.Nop) 9. ' Try 10. generator.BeginExceptionBlock() 11. generator.Emit(OpCodes.Ldarg_0) 12. generator.EmitCall(OpCodes.Callvirt, enumer, Nothing) 13. generator.Emit(OpCodes.Stloc_1) 14. Dim IL_000b As Label = generator.DefineLabel() 15. Dim IL_003f As Label = generator.DefineLabel() 16. generator.Emit(OpCodes.Br, IL_003f) ' BR_S is wrong 17. generator.MarkLabel(IL_000b) 18. generator.Emit(OpCodes.Ldloc_1) 19. generator.EmitCall(OpCodes.Callvirt, get_Current, Nothing) 20. ' added this 21. generator.Emit(OpCodes.Castclass, GetType(T)) 22. generator.Emit(OpCodes.Stloc_0) 23. ' write each property 24. For Each prop As PropertyInfo In properties 25. generator.Emit(OpCodes.Ldstr, prop.Name + "=") 26. generator.Emit(OpCodes.Ldloc_0) 27. Dim get_Prop As MethodInfo = _ GetType(T).GetMethod("get_" + prop.Name, New Type() {}) 28. generator.EmitCall(OpCodes.Callvirt, get_Prop, Nothing) 29. ' Handle value types that need to be boxed for ToString 30. If (prop.PropertyType.IsValueType) Then a. generator.Emit(OpCodes.Box, prop.PropertyType) 31. End If 32. generator.Emit(OpCodes.Callvirt, _ GetType(Object).GetMethod("ToString", New Type() {})) 33. generator.Emit(OpCodes.Call, concat) 34. generator.Emit(OpCodes.Call, write) 35. generator.Emit(OpCodes.Nop) 36. Next 37. generator.Emit(OpCodes.Nop) 38. generator.MarkLabel(IL_003f) 39. generator.Emit(OpCodes.Ldloc_1) 40. generator.EmitCall(OpCodes.Callvirt, MoveNext, Nothing) 41. generator.Emit(OpCodes.Stloc_2) 42. generator.Emit(OpCodes.Ldloc_2) 43. ' Brtrue_S is wrong 44. generator.Emit(OpCodes.Brtrue, IL_000b) 45. generator.Emit(OpCodes.Nop) 46. Dim IL_0060 As Label = generator.DefineLabel() 47. generator.Emit(OpCodes.Leave_S, IL_0060) 48. ' begin finally 49. generator.BeginFinallyBlock() 50. generator.Emit(OpCodes.Ldloc_1) 51. generator.Emit(OpCodes.Ldnull) 52. generator.Emit(OpCodes.Ceq) 53. generator.Emit(OpCodes.Ldc_I4_0) 54. generator.Emit(OpCodes.Ceq) 55. generator.Emit(OpCodes.Stloc_2) 56. generator.Emit(OpCodes.Ldloc_2) 57. Dim IL_005e As Label = generator.DefineLabel 58. generator.Emit(OpCodes.Brfalse_S, IL_005e) 59. generator.Emit(OpCodes.Ldloc_1) 60. Dim dispose As MethodInfo = _ GetType(System.IDisposable).GetMethod("Dispose", New Type() {}) 61. generator.EmitCall(OpCodes.Callvirt, dispose, Nothing) 62. generator.Emit(OpCodes.Nop) 63. generator.MarkLabel(IL_005e) 64. generator.Emit(OpCodes.Nop) 65. generator.EndExceptionBlock() 66. generator.MarkLabel(IL_0060) 67. generator.Emit(OpCodes.Nop) 68. generator.Emit(OpCodes.Ret)
Line 2 gets all of the properties for type T, which in the example is a Customer. Line 3 requests an ILGenerator from the MethodBuilder. The ILGenerator will convert the symbolic OpCodes to actual MSIL.
Lines 4, 5, and 6 define local variables for the dynamic method. There is the local variable representing a Customer object, an instance of IEnumerator(Of T), and a Boolean.
Lines 7 and 8 generate a Nop (pronounced no-op). These placeholders do nothing other than add one byte padding. I believe, although on this I could be wrong (sorry), their purpose is to add a little padding so instructions break on even 32-bit boundaries, which helps with moving the instruction pointer and other registers to locations that match the size of microprocessor registers.
Page 4 of 5
This article was originally published on October 28, 2008