Visual Studio 2010!

Read now >

View Now
DevSource RSS FEEDS
XML Want an easy way to keep up with breaking tech news? And the Get DevSource headlines delivered to your desktop with RSS.
ADVERTISEMENT
ADVERTISEMENT

 

DevSource.com: Your Source for Visual Studio on Facebook
ADVERTISEMENT
Programming a Dynamic Method in C#
By Paul Kimmel

Rate This Article: Add This Article To:

Programming a Dynamic Method in C# - DynamicMethod Deep Copy
( Page 4 of 4 )

Implementing a DynamicMethod Deep Copy Emitter

The stated goal becomes to get the performance characteristics of the hard-coded behavior with the programmer effort being closer to the reflective version of the code. The solution can be to use the DynamicMethod and write a code generator that generates the equivalent of the hard-coded algorithm. The result is that the programmer writes one solution and the generator writes code on behalf of the programmer depending on the type.

For our purposes the code generator will code that uses the DynamicMethod and emits MSIL. The code in Listing 4 accepts a type T and then emits a member-by-member deep copy method for that object type. The resulting MSIL performs the same operation as the code in listing 2. Clearly from the code in Listing 4 the emitter is harder to write, but you only have to write it once and you can use it in any number of future applications with as-yet undefined objects.





Listing 4: The emitter in the listing uses the DynamicMethod class and emits code that does a member-by-member deep copy.
private delegate T DeepCopier<T>(T source);
public static DynamicMethod CopyEmitter<T>(T source)
{

  // define the method name
  string name = "Copy" + typeof(T).Name;

  DynamicMethod method = new DynamicMethod(name, typeof(T),
    new Type[] { typeof(T) });

  ILGenerator il = method.GetILGenerator();

  LocalBuilder obj0 = il.DeclareLocal(typeof(T)); //target

  // create object and store in local 0
  ConstructorInfo ctor = typeof(T).GetConstructor(
      new Type[] { });
  il.Emit(OpCodes.Newobj, ctor);
  il.Emit(OpCodes.Stloc_0);


  PropertyInfo[] properties = typeof(T).GetProperties(BindingFlags.Instance
    | BindingFlags.Public | BindingFlags.FlattenHierarchy);
  foreach (PropertyInfo prop in properties)
  {
    // local constructed object
    il.Emit(OpCodes.Ldloc_0);

    // load source argument
    il.Emit(OpCodes.Ldarg_0);

    // get property value
    il.EmitCall(OpCodes.Callvirt, typeof(T).GetMethod(
        "get_" + prop.Name), null);
    il.EmitCall(OpCodes.Callvirt, typeof(T).GetMethod(
        "set_" + prop.Name), null);
  }

  il.Emit(OpCodes.Ldloc_0);
  il.Emit(OpCodes.Ret);
return method;
}

The code accepts an object of type T and returns a DynamicMethod. The first thing it does is create a DynamicMethod, specifying the name, return type and argument type. Form the DynamicMethod an ILGenerator is requested.

Next, a local variable of type T is defined. This will be the deeply cloned object to return. After the local variable is defined a ConstructorInfo object for that type is created and the object is constructed and stored in the local variable. The following three lines perform this function:

ConstructorInfo ctor = typeof(T).GetConstructor(
    new Type[] { });
il.Emit(OpCodes.Newobj, ctor);
il.Emit(OpCodes.Stloc_0);

The next step is to get the properties for the type T. Iterating over each property the newly constructed object is grabbed

il.Emit(OpCodes.Ldloc_0);

the incoming argument parameter is loaded

il.Emit(OpCodes.Ldarg_0);

and the PropertyInfo object is used to get the getter and setter method and emit a call to those methods. The getter is invoked against the incoming source object and the setter is invoked against the locally created and stored copy.

Finally, the return object, the copy is preppred to return and the function return is emitted. The last step is to return the DynamicMethod

  il.Emit(OpCodes.Ldloc_0);
  il.Emit(OpCodes.Ret);
return method;

To use the DynamicMethod call CreateDelegate, assigning the returned delegate to a delegate variable, and then call the delegate like a method (see Listing 5).

Listing 5: Emitting and using the DynamicMethod.

  DynamicMethod dynaMethod = CopyEmitter(dc);
  DeepCopier<DeepCopyMe> copier =
    (DeepCopier<DeepCopyMe>)dynaMethod.CreateDelegate(    
    typeof(DeepCopier<DeepCopyMe>));

  for (int i = 0; i < 3; i++)
  {
    dc.IntegerProperty = rnd.Next();
    Stopwatch watch = Stopwatch.StartNew();
    DeepCopyMe copy2 = copier(dc);
    Console.WriteLine("Emitted : " + watch.Elapsed.ToString());
    Console.WriteLine(copy2);
  }

  Console.ReadLine();

To use this emitter for other types call CopyEmitter for each type and stored the resulting DynamicMethod. The reason I put the sample code in a loop is because the first call’s performance will be poor because the JIT compiler will have to compile the MSIL. After the first call to the DynamicMethod the performance should approach that of the hard-coded algorithm. Using the Stopwatch to compare the hard-coded, reflected, and emitted versions, the hard-coded and emitted versions were substantially faster than the reflected version and the emitted version actually performed faster than the C# hard-coded version of the deep copy.

You can learn to write MSIL by writing regular C# or VB.NET code and then looking at the IL with ILDASM. You can also use tools like ilasm.exe and MDbg.exe to debug MSIL.

Summary

Hard-coded algorithms that dump an object’s state or perform deep copies are fast but work with only one object. General algorithms that use reflection to work with any object are slow. Using the DynamicMethod and Reflection.Emit you can write dynamic code—the benefit of a general algorithm—that emits the equivalent of the hard-code behavior—with the speed of the hard-coded algorithm.

There is a drawback to writing emitters: they are harder to write and harder to debug. So consider replacing general, Reflection code with emitted hard-code when the Reflection code is making your application sluggish.

Biography

Paul Kimmel is the VB Today columnist for www.codeguru.com and has written several books on object-oriented programming and .NET. Check out his book LINQ Unleashed for C#. It is currently available online at safari.informit.com as a rough cut (digital PDF format). Paul is an application architect for Electronic Data Systems in Lansing, Michigan. You may contact him for technology questions at pkimmel@softconcepts.com.

If you are interested in joining or sponsoring a .NET Users Group, check out www.glugnet.org.



 
 
>>> More Microsoft Languages Articles          >>> More By Paul Kimmel