Fun with dynamics in c#

“Dynamic” keyword was introduced in .net framework 4.0. Recently I got a chance to explore dlr and c# dynamics in more depth and I think its magic.

In this post I am going to show you some of the interesting things you can do with dynamics in c#

if you want to read more about .net Dynamics and DLR in general you can do that here:

http://visualstudiomagazine.com/articles/2011/02/01/understanding-the-dynamic-keyword-in-c4.aspx

Dynamic c# reference

 

Ok, assuming you have read up a bit on it, or kinda know it already,  Lets Start With a Simple Class

public class Person
{
   public Person()
        {
            Name = "Preet";
        }
        public string Name { get; set; }
    }

The class above has only one property Name, We should be able to add more functionality to this class.

We should be able to do something like this:

dynamic person =  DynamicDecorator<Person>.GetDecorated(new Person());
            person.Age = 35;
            person.AgeFormatter = new Func<dynamic, string>
                                    (p => string.Format("{0} your age is {1}", p.Name, p.Age));
            Console.WriteLine(person.Name);
            Console.WriteLine(person.Age);
            Console.WriteLine(person.AgeFormatter(person));
            person.SetInterceptor("AgeFormatter", new Dummy());
            Console.WriteLine(person.AgeFormatter(person));
            Console.ReadKey();

For this we need some form of decorator, we can use to add functionality/behavior to this class:

public class DynamicDecorator<T> : DynamicObject
    {
        /// <summary>
        /// This is the object we are decorating or going to add properties to it
        /// </summary>
        public T ObjectToDecorate { get; set; }
        /// <summary>
        /// A bag where we put all the existing and new properties
        /// </summary>
        private Dictionary<string, object> _properties;
        /// <summary>
        /// A bag where we store all our interceptors or may be method call forwarders
        ///(*** This                     needs to be a bit better***)
        /// </summary>
        private Dictionary<string, object> _interceptors;
         /// <summary>
        /// As we are decorating a static class we need a constructor that takes in a type, 
        ///so we can determine existing properties and methods
        /// </summary>
        /// <param name="objectToDecorate"></param>
        private DynamicDecorator(T objectToDecorate)
        {
            ObjectToDecorate = objectToDecorate;
            _properties = new Dictionary<string, object>();
            _interceptors = new Dictionary<string, object>();
            AddExistingProperties();
        }

        /// <summary>
        /// This goes through the existing properties and adds them to the bag
        /// </summary>
        private void AddExistingProperties()
        {
            var existingProperties = ObjectToDecorate.GetType().GetProperties().ToList();
            foreach (var existingProperty in existingProperties)
            {
                _properties.Add(existingProperty.Name, 
                                existingProperty.GetValue(ObjectToDecorate));
            }
        }

        /// <summary>
        /// Set all the interceptors
        /// </summary>
        /// <param name="memberToIntercept"></param>
        /// <param name="d"></param>
        public void SetInterceptor(string memberToIntercept, object d)
        {
            if (_interceptors.ContainsKey(memberToIntercept))
            {
                _interceptors[memberToIntercept] = d;
                return;
            }
            _interceptors.Add(memberToIntercept, d);

        }

        /// <summary>
        /// Get the original item as dynamic so we can bolt on extra stuff on it
        /// </summary>
        /// <param name="person"></param>
        /// <returns></returns>

        public static dynamic GetDecorated(T person)
        {
            return new DynamicDecorator<T>(person);
        }

        /// <summary>
        /// This is whhat gets executed when you try to get a member on the decorated type
        /// </summary>
        /// <param name="binder"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {


                return _properties.TryGetValue(binder.Name, out result);


        }

        /// <summary>
        /// When you try to add a new member to the decorated type, this is what gets executed
        /// </summary>
        /// <param name="binder"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            if (_properties.ContainsKey(binder.Name))
            {
                _properties[binder.Name] = value;
            }
            _properties.Add(binder.Name, value);
            return true;
        }

        /// <summary>
        /// This is what happens when you try to invoke a member on the Decorated type
        /// </summary>
        /// <param name="binder"></param>
        /// <param name="args"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public override bool TryInvokeMember(InvokeMemberBinder binder, 
                                               object[] args, out object result)
        {
            // check if the interceptor has been set for this member
            if (_interceptors.ContainsKey(binder.Name))
            {
                var toInvoke = _interceptors[binder.Name];
                    var method = toInvoke.GetType().GetMethods()
                                         .FirstOrDefault(info => 
                                           info.Name.Contains(binder.Name));
                    result = method.Invoke(toInvoke, args);
                    return true;

            }
            return base.TryInvokeMember(binder, args, out result);
        }
    }

This produces the following output:

 

DynamicObject Example output

 

Now for the explanation of the events:

 

Leave a Reply

%d bloggers like this: