The Imaginative Universal

Studies in Virtual Phenomenology -- @jamesashley

Addendum to Dynamic Method Bags

November 22
by James Ashley 22. November 2009 23:10

Bill Wagner has an interesting article on MSDN about implementing Dynamic Objects in C# 4: http://msdn.microsoft.com/en-us/library/ee658247.aspx.  He leaves it up to the reader to figure out the right way to implement the class he describes and provides suggestions.  This is a great way to structure a propaedeutic of this sort, as it affords the clever reader an opportunity to dig into the material on her own.

At the same time, it is often helpful to have a key against which to compare one’s own work.  To that end, the full code required to run the sample application described in the article follows.

Here is the code that serves as our test.  Bill Wagner describes an object that inherits from DynamicObject that will run the following code:

    var newType = new MethodBag();
    newType.SetMethod("Write", () => Console.WriteLine("Hello World"));
    newType.SetMethod("Display", (string parm) => Console.WriteLine(parm));
    newType.SetMethod("IsValid", () => true);
    newType.SetMethod("Square", (int num) => num * num);
    newType.SetMethod("Sequence", () => from n in Enumerable.Range(1, 100)
                                        where n % 5 == 2
                                        select n * n);
    dynamic dispatcher = newType;
    dispatcher.Write();
    var result = dispatcher.IsValid();
    Console.WriteLine(result);
    dispatcher.Display("This is a message");
    var result2 = dispatcher.Square(5);
    Console.WriteLine(result2);
    var sequence = dispatcher.Sequence();
    foreach (var num in sequence)
        Console.WriteLine(num);

    Console.ReadLine();

 

Here is an implementation of MethodBag that will fulfill the expectations established above:

internal class MethodBag: DynamicObject
{
    private Dictionary<string, MethodDescription> methods = 
        new Dictionary<string, MethodDescription>();

    #region Method Descriptions

    private abstract class MethodDescription
    {
        internal abstract int NumberOfParameters
        {
            get;
        }
        internal Expression target
        {
            get;
            set;
        }
        internal abstract object Invoke(object[] parms);
    }

    private class ActionDescription: MethodDescription
    {
        internal override int NumberOfParameters
        {
            get { return 0; }
        }
        internal override object Invoke(object[] parms)
        {
            var target2 = target as Expression<Action>;
            target2.Compile().Invoke();
            return null;
        }
    }

    private class ActionDescription<T> : MethodDescription
    {
        internal override int NumberOfParameters
        {
            get { return 1; }
        }
        internal override object Invoke(object[] parms)
        {
            dynamic target2 = target;
            target2.Compile().Invoke(parms[0]);
            return null;
        }
    }

    private class FuncDescription<T> : MethodDescription
    {
        internal override int NumberOfParameters
        {
            get { return 0; }
        }
        internal override object Invoke(object[] parms)
        {
            dynamic target2 = target;
            return target2.Compile().Invoke();
        }
    }

    private class FuncDescription<T,S> : MethodDescription
    {
        internal override int NumberOfParameters
        {
            get { return 1; }
        }
        internal override object Invoke(object[] parms)
        {
            dynamic target2 = target;
            return target2.Compile().Invoke(parms[0]);

        }
    }

    #endregion

    public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args
        , out object result)
    {
        result = null;
        if (!methods.ContainsKey(binder.Name))
            return false;
        // Described later
        MethodDescription method = methods[binder.Name];
        if (method.NumberOfParameters != args.Length)
            return false;
        result = method.Invoke(args);
        return true;

    }

    #region Set Methods

    public void SetMethod(string name, Expression<Action> lambda)
    {
        var desc = new ActionDescription { target = lambda };
        methods.Add(name, desc);
    }

    public void SetMethod<T>(string name, Expression<Action<T>> lambda)
    {
        var desc = new ActionDescription<T> { target = lambda };
        methods.Add(name, desc);
    }

    public void SetMethod<T>(string name, Expression<Func<T>> lambda)
    {
        var desc = new FuncDescription<T> { target = lambda };
        methods.Add(name, desc);
    }
    
    public void SetMethod<T,S>(string name, Expression<Func<T,S>> lambda)
    {
        var desc = new FuncDescription<T,S> { target = lambda };
        methods.Add(name, desc);
    }

    #endregion
}

 

Your output should look like this:

Hello World
True
This is a message
25
4
49
144
289
484
729
1024
1369
1764
2209
2704
3249
3844
4489
5184
5929
6724
7569
8464
9409

Tags: ,

.NET 4.0 | C#