Dumping an Object's State with a DynamicMethod, Page 2
Table 1: Comparing algorithms and relative performance
|Solution||General Use||Relative Speed||JIT Compile||Assembly Unloaded|
The code in Listing 1 starts by creating a DynamicMethod that will accept an argument of IEnumerable(Of T) and a TextWriter although the TextWriter is not used in the emitted code. Local variables of type MethodInfo are initialized for methods the emitter will need, such as Console.WriteLine, String.Concat, IEnumerator.MoveNext, IEnumerable.GetEnumerator, and IEnumerator.get_Current. Next, you need to request an ILGenerator from the DynamicMethod. An ILGenerator is used to convert OpCodes and things to MSIL.
Next, some local variables are declared. These will contain type information, an IEnumerator(Of T), and a Boolean. The Nop is hard to pin down but Amanda Silver told me that they are used to permit breakpoints on non-executable lines of code.
BeginExceptionBlock is where you start a try..catch..finally block in MSIL. The rest of the code basically contains an enumerator for the passed-in collection. The PropertyInfo is obtained for the type T in the collection and each of the properties and property values are requested and sent to the Console in a loop. The If(prop.PropertyType.IsValueType checks to see if the property type is a value type. If it is the code boxes the property—wraps an obejct around it—so it can call ToString on the value type. This code will execute for things like integers, decimals, basically value types. After each property and value is concatenated and sent to the console, the next object in the collection is obtained from the enumerator and the process repeats. Eventually, the collection will run out of obejcts and cleanup occurs in the finally block. The last step is to return the DynamicMethod so the consuming code can use it.
Using the DynamicMethod State Dumper
The Main method creates a list of customers. Any sample data or type will do. After the customers collection has been created, the DynamicMethodCreator.CreateDumpDelegate is called by passing in the list of customers. The returned DynamicMethod is used to create a delegate and assign it to the local delegate named MyDelegate.
Finally, MyDelegate is used like a regular old method. The only change you'd need to make if you wanted to dump Orders or Widgets instead of customers would be to initiatize an additional new variable using the new type; instead of mydelegate As DumpDelegate(Of Customer), replace Customer with the new type you'd like to dump.
Listing 2: Sample code to use the DynamicMethod.
Imports System.Reflection Imports System.IO Imports System.Runtime.CompilerServices Imports System.Reflection.Emit Module Module1 Sub Main() ' Create customer list Dim customers As List(Of Customer) = New List(Of Customer) For i As Integer = 0 To 50 customers.Add(New Customer(i.ToString())) Next Dim method As DynamicMethod = _ DynamicMethodCreator.CreateDumpDelegate(customers) MyDelegate = method.CreateDelegate( _ GetType(DumpDelegate(Of Customer))) MyDelegate(customers, Console.Out) Console.ReadLine() End Sub Private MyDelegate As DumpDelegate(Of Customer) Private Delegate Sub DumpDelegate(Of T)( _ ByVal list As IEnumerable(Of T), ByVal writer As TextWriter) End Module
If you are asking yourself whether the extra effort is worth the benefit, run some comparisons on the different implementation types in Table 1. The emitted hard-coded example was generally hundreds and in some cases thousands of times faster than the plain old Reflection version. And remember, you only have to write an emitter one time.
You can hard code a solution over and over, but it's going to cut into your time budget. You can use plain vanilla reflection, but it's going to cut into your performance budget. You can use dynamically emitted assemblies to improve workload and speed things up, but you'll pay in the form of memory. If you write a dynamic emitter that uses a DynamicMethod, you save time, improve performance, and your memory won't suffer. And remember, emitters can be used over and over.
About the Author
Paul Kimmel is a freelance writer for Developer.com and CodeGuru.com. He is the founder of Software Conceptions, Inc, founded in 1990. Paul Kimmel is architect for EDS, an HP Company. You may contact him about article questions at email@example.com.
Check out Paul's most recent books, LINQ Unleashed and Teach Yourself the ADO.NET Entity Framework in 24 Hours (coming Spring 2009).
Copyright © 2008 by Paul T. Kimmel. All Rights Reserved.