Attaching Dynamic Methods

Yesterday I did a bit of working using DynamicMethod. I have to admit, that was the first time I touched that class. I like diving into the Emit namespace, but I haven't had the need to crank out a shim method like that. Anyway, as I was reading the documentation, I found this:

If the dynamic method is associated with a type, it has access to the private members of the type.

Just what does that mean, exactly? It smelled like something that I've seen in JavaScript:

function State(value)
{
    this.__value__ = value;
}

State.prototype.GetValue = function()
{
    return this.__value__;
};

var s = new State(10);
WScript.Echo(s.GetValue());
WScript.Echo(s.__value__);

try
{
    s.SetValue(20);
}
catch(e)
{
    WScript.Echo(e.message);
}

State.prototype.SetValue = function(value)
{
    this.__value__ = value;
};

s.SetValue(20);
WScript.Echo(s.GetValue());

This shows how calling a function SetValue doesn't work until the prototype is set up. Running this under wscript gives this:

10
10
Object doesn't support this property or method
20

This isn't anything new for developers who use dynamic languages (ala Python, Ruby, etc.). But I was curious to see if DynamicMethod gives similar behavior:

internal static class Program
{
    private delegate void SetValueClassDelegate(State state, int value);
    private delegate void SetValueObjectDelegate(int value);
    
    internal static void Main(string[] args)
    {
        State state = new State(10);
        Console.Out.WriteLine(state.Value);
        SetValueObjectDelegate setValueObject = Program.AttachMethodToObject(state);
        setValueObject(20);
        Console.Out.WriteLine(state.Value);
            
        State state2 = new State(100);
        Console.Out.WriteLine(state2.Value);
        setValueObject(200);
        Console.Out.WriteLine(state2.Value);

        SetValueClassDelegate setValueClass = Program.AttachMethodToClass();
        State state3 = new State(1000);
        Console.Out.WriteLine(state3.Value);
        setValueClass(state3, 2000);
        Console.Out.WriteLine(state3.Value);
    }

    private static SetValueObjectDelegate AttachMethodToObject(State state)
    {
        Type[] arguments = { typeof(State), typeof(int) };
        DynamicMethod converter = new DynamicMethod("SetValueObject",
             null, arguments, typeof(State));

        ILGenerator generator = converter.GetILGenerator();
        generator.Emit(OpCodes.Ldarg_0);
        generator.Emit(OpCodes.Ldarg_1);
        generator.Emit(OpCodes.Stfld, 
            typeof(State).GetField("value", BindingFlags.NonPublic | BindingFlags.Instance));
        generator.Emit(OpCodes.Ret);

        return (SetValueObjectDelegate)converter.CreateDelegate(typeof(SetValueObjectDelegate), state);
    }

    private static SetValueClassDelegate AttachMethodToClass()
    {
        Type[] arguments = { typeof(State), typeof(int) };
        DynamicMethod converter = new DynamicMethod("SetValueClass",
             null, arguments, typeof(State));

        ILGenerator generator = converter.GetILGenerator();
        generator.Emit(OpCodes.Ldarg_0);
        generator.Emit(OpCodes.Ldarg_1);
        generator.Emit(OpCodes.Stfld,
            typeof(State).GetField("value", BindingFlags.NonPublic | BindingFlags.Instance));
        generator.Emit(OpCodes.Ret);

        return (SetValueClassDelegate)converter.CreateDelegate(typeof(SetValueClassDelegate));
    }
}

Where State looks like this:

internal sealed class State
{
    private int value;
    
    internal State() : base()
    {
    }
    
    internal State(int value) : this()
    {
        this.value = value;
    }
    
    internal int Value
    {
        get
        {
            return this.value;
        }
    }
}

Running this code gives the following:

10
20
100
100
1000
2000

What this shows to me is that you can add new methods to specific object instances (you can also do this in JavaScript - I just didn't show that in my code example). However, you can't attach a method to a class such that it's a part of every class instance (for some reason I think a Smalltalker is telling me, "if only a class was an object..."). You can attach a method to a type - in this case, it acts like a static method. In either case, both methods have access to the internals of the object. I tried using InvokeMethod() to see if I could "find" the new method, and that doesn't seem to work (although my code could be wrong too).

Dynamic methods are another cool aspect of .NET. Just do a search on "DynamicMethod" and you'll find all sorts of cool little nuggests out there. If your code has the right permissions, you're never limited by your language; you can always get around limitations by visiting the Emit namespace.

* Posted at 03.27.2007 08:30:24 PM CST | Link *

Blog History