Poor Man's Singleton Methods via Dynamic Wrappers in C#

By Anoop Madhusudanan

Vote on HN

image "The static world you see there is not real, you are programmed to think so. There is a true world out there, which is very very dynamic" - Do I sound like Morpheus in Matrix?


Justin recently wrote an emotional post about Ruby, and as a primary C# guy, I found it quite interesting. Though I’ve never paid some serious attention to Ruby earlier, I was exploring the dynamic features in C# for some time (See my posts on dynamic and also have a look at this ElasticObject implementation). So, I started digging into Ruby a bit after reading Justin’s post, I found a lot of interesting concepts, for example, the Singleton methods.

Singleton Methods

So, what are Singleton methods?

The behavior of an instance is determined by its class, but there may be times we know that a particular instance should have special behavior. In most languages, we must go to the trouble of defining another class, which would then only be instantiated once. In ruby we can give any object its own methods.

And here is a Ruby example.

ruby> class SingletonTest
    |   def size
    |     25
    |   end
    | end
   nil
ruby> test1 = SingletonTest.new
   #<SingletonTest:0xbc468>
ruby> test2 = SingletonTest.new
   #<SingletonTest:0xbae20>
ruby> def test2.size
    |    10
    | end
   nil
ruby> test1.size
   25
ruby> test2.size
   10

See that you are 're-opening' the size method of test2 object, to return some other value other than the value specified by the default size method, at runtime. Isn’t that interesting? For sure, especially if you hasn't visited Ruby earlier.

Poor man's Singleton Methods In C#

Now, I started thinking about implementing something similar in C#, leveraging the dynamic features, with out depending on the traditional AOP style, and came up with a dynamic wrapper that’ll allow you to do wrap objects, some what like a proxy. You can assign a new delegate instance against an existing method name, and the wrapper is capable of re-routing invocations to the assigned method when an invocation happens. Ah, I think some code is better than the explanation.

For example, consider this code.

class Program
    {
        static void Main(string[] args)
        {

            //Existing objects
            Duck someObject = new Duck();
            dynamic duck = someObject.Extend();

            //Call Walk
            duck.Walk();

            //Now modify this Walk() on the fly
            duck.Walk = new Action(() => System.Console.WriteLine("Duck Walking left"));

            //Call Walk again
            duck.Walk();

        }
    }
    public class Duck
    {
        public void Walk()
        {
            System.Console.WriteLine("Duck walking right");
        }
    }
You can see that we are modifying the Walk behaviour, and imagine the output you'll get.

Duck Walking right
Duck Walking left

As you might imagine, the Extend extension method simply creates a dynamic wrapper on top of our duck object, and returns the wrapper for further operations. The wrapper can also wrap static methods, which means you can do something like this as well.

//Static classes
            dynamic con = typeof(System.Console).Extend();
            con.WriteLine=new Action<string>
                             (s=>System.Console.WriteLine("hello " + s));
            con.WriteLine(" world");
            
           //Your output will be 'hello world' (with out the quotes)

The only problem here is, once you extend the object via the dynamic wrapper, you need the whole infrastructure to be dynamic to leverage this, while in AOP your proxies are still typed. So assuming your program universe has two worlds, say static and dynamic, the wrapped object is of no use in the static world – all this will work only in the dynamic world (say your functions/properties accepting dynamic objects and returning dynamic objects etc).

Also, comparing to Ruby, we are not even near - we are not touching the actual object or modifying any behavior of the same, we are just wrapping it :(

Now, if you don't need to wrap existing classes/objects in the .NET framework, you may use the ExpandoObject to attach your own behavior

Another interesting thought


The wrapper can be used to wrap the C# objects, and pass the same to dynamic worlds so that the behavior of your C# objects can be modified there - like, when you pass an object from C# to IronRuby. I havn't tried it yet and I have no idea how it works in the IronRuby world right now, but it should be possible. :)

The Code - Exploring further

So, here are the extension methods and the wrapper, that'll achieve what I showed above.

//Warning: Just wrote this pretty quickly, only for demo purposes :)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Dynamic;
using System.Reflection;

namespace DynamicLibrary
{

 public static class DynamicExtensions
    {
        //Our generic IsGreaterThan extension method
        public static dynamic Extend<T>(this T obj)
        {
            return new ExtensionWrapper(obj);
        }

        public static dynamic New<T>(this T t)  where T:new()
        {
            return new ExtensionWrapper(new T());
        }

    }

    public class ExtensionWrapper : DynamicObject
    {

        /// <summary>
        /// The object we are going to wrap
        /// </summary>
        object _wrapped;

        /// <summary>
        /// A collection of methods
        /// </summary>
        Dictionary<string, Delegate> _methods = new Dictionary<string, Delegate>();

        /// <summary>
        /// Specify the flags for accessing members
        /// </summary>
        static BindingFlags flags = BindingFlags.Instance
            | BindingFlags.Static | BindingFlags.Public;

        /// <summary>
        /// Create a simple private wrapper
        /// </summary>
        public ExtensionWrapper(object o)
        {
            _wrapped = o;
        }


        public ExtensionWrapper(Type t)
        {
            _wrapped = t;
        }


        Type WrappedType
        {
            get
            {
                return (_wrapped is Type) 
                    ? _wrapped as Type 
                    : _wrapped.GetType();
            }
        }

        /// <summary>
        /// Try invoking a method
        /// </summary>
        public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
        {
            var types = from a in args
                        select GetActualType(a);
            string mname = GetInternalName(binder.Name, args);

            if (_methods.ContainsKey(mname))
            {
                result = _methods[mname].DynamicInvoke(args);
                return true;
            }

            var method = WrappedType.GetMethod
                (binder.Name, flags, null, types.ToArray(), null);

            if (method == null)
                return base.TryInvokeMember(binder, args, out result);
            else
            {
                result = method.Invoke(_wrapped, args);
                return true;
            }
        }

        public Type GetActualType(object t)
        {
            return (t is ParameterInfo)
                        ? (t as ParameterInfo).ParameterType : t.GetType();
        }

        /// <summary>
        /// Get the internal name for a method for housekeeping
        /// </summary>
        private string GetInternalName(string name, object[] args)
        {

               var typeNames = from a in args
                            select GetActualType(a).Name;

            string mname = name + "$" +
                                        (typeNames.Count() > 0
                                            ? typeNames.Aggregate((a, b) => a + b)
                                            : string.Empty);
            return mname;
        }

        /// <summary>
        /// Tries to get a property or field with the given name
        /// </summary>
        public override bool TryGetMember(System.Dynamic.GetMemberBinder binder, out object result)
        {
            //Try getting a property of that name
            var prop = WrappedType.GetProperty(binder.Name, flags);

            if (prop == null)
            {
                //Try getting a field of that name
                var fld = WrappedType.GetField(binder.Name, flags);
                if (fld != null)
                {
                    result = fld.GetValue(_wrapped);
                    return true;
                }
                else
                    return base.TryGetMember(binder, out result);
            }
            else
            {
                result = prop.GetValue(_wrapped, null);
                return true;
            }
        }

        /// <summary>
        /// Tries to set a property or field with the given name
        /// </summary>
        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            var prop = WrappedType.GetProperty(binder.Name, flags);
            if (prop == null)
            {
                var fld = WrappedType.GetField(binder.Name, flags);
                if (fld != null)
                {
                    fld.SetValue(_wrapped, value);
                    return true;
                }
                else if (value is Delegate)
                {
                    var param = (value as Delegate).Method.GetParameters();
                    var types = from a in param
                                select GetActualType(a);
                    var method = WrappedType.GetMethod
                                        (binder.Name, flags, null, types.ToArray(), null);

                    if (method != null)
                    {
                        string mname = GetInternalName(binder.Name, param);                         
                        _methods[mname] = value as Delegate;
                        return true;

                    }
                    else
                    {
                        return base.TrySetMember(binder, value);
                    }

                }
                else
                    return base.TrySetMember(binder, value);
            }
            else
            {
                prop.SetValue(_wrapped, value, null);
                return true;
            }
        }

    }
}

This might be insane and of no use, but never mind. Happy coding!! Have a look at my other posts on C# dynamic features
© 2012. All Rights Reserved. Amazedsaint.com