Archive for the ‘dynamic’ Category

C# 4.0: Exposer, an evil DynamicObject

This class makes every field, property, or method on the wrapped object visible when using it as a dynamic. This version is not thread safe, for the sake of brevity I removed all of the locks. To use, you only need to add this to your project, then call .Expose() on an instance of some object, then assign that to a dynamic variable, like so:

dynamic x = someInstance.Expose();

You may then access any field, property, or method regardless of it’s visibility level. Feel free to comment with questions if anything needs explanation ;).

 

Code Snippet
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Dynamic;
  4. using System.Linq;
  5. using System.Linq.Expressions;
  6. using System.Reflection;
  7. using System.Text;
  8.  
  9. namespace Evil
  10. {
  11.     public interface IConvertTo<T>
  12.     {
  13.         T Convert();
  14.     }
  15.  
  16.     public interface IObjectWithType
  17.     {
  18.         Type Type { get; set; }
  19.     }
  20.  
  21.     public class Exposer<T> : DynamicObject, IObjectWithType, IConvertTo<T>
  22.     {
  23.         public T Object { get; set; }
  24.         public Type Type { get; set; }
  25.  
  26.         static Dictionary<string, Func<T, object[], object>> _methods = new Dictionary<string, Func<T, object[], object>>();
  27.         static Dictionary<string, Func<T, object>> _getters = new Dictionary<string, Func<T, object>>();
  28.         static Dictionary<string, Action<T, object>> _setters = new Dictionary<string, Action<T, object>>();
  29.  
  30.         static MethodInfo _doConvert = typeof(Exposer<T>).GetMethod(“DoConvert”, BindingFlags.NonPublic | BindingFlags.Static);
  31.  
  32.         public Exposer(T obj)
  33.         {
  34.             this.Object = obj;
  35.             this.Type = obj.GetType();
  36.         }
  37.  
  38.         public override bool TryGetMember(GetMemberBinder binder, out object result)
  39.         {
  40.             var key = binder.Name;
  41.             Func<T, object> getter = null;
  42.  
  43.             if (_getters.ContainsKey(key))
  44.             {
  45.                 getter = _getters[key];
  46.             }
  47.             else
  48.             {
  49.                 IEnumerable<MemberInfo> members = this.Type.GetMembers(
  50.                     BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetProperty | BindingFlags.GetField);
  51.  
  52.                 members = from mem in members
  53.                           where mem.Name == key
  54.                           select mem;
  55.  
  56.                 var member = members.FirstOrDefault();
  57.  
  58.                 if (member != null)
  59.                 {
  60.                     getter = BuildGetter(member);
  61.                     _getters.Add(key, getter);
  62.                 }
  63.             }
  64.  
  65.             if (getter != null)
  66.             {
  67.                 result = Wrap(getter(this.Object));
  68.                 return true;
  69.             }
  70.             else
  71.                 return base.TryGetMember(binder, out result);
  72.         }
  73.  
  74.         public override bool TrySetMember(SetMemberBinder binder, object value)
  75.         {
  76.             var key = binder.Name;
  77.             Action<T, object> setter = null;
  78.  
  79.             if (_setters.ContainsKey(key))
  80.             {
  81.                 setter = _setters[key];
  82.             }
  83.             else
  84.             {
  85.                 IEnumerable<MemberInfo> members = this.Type.GetMembers(
  86.                     BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.SetProperty | BindingFlags.SetField);
  87.  
  88.                 members = from mem in members
  89.                           where mem.Name == key
  90.                           select mem;
  91.  
  92.                 var member = members.FirstOrDefault();
  93.  
  94.                 if (member != null)
  95.                 {
  96.                     setter = BuildSetter(member);
  97.                     _setters.Add(key, setter);
  98.                 }
  99.             }
  100.  
  101.             if (setter != null)
  102.             {
  103.                 setter(this.Object, value);
  104.                 return true;
  105.             }
  106.             else
  107.                 return base.TrySetMember(binder, value);
  108.         }
  109.  
  110.         public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
  111.         {
  112.             Func<T, object[], object> func = null;
  113.  
  114.             var key = MakeKey(binder, args);
  115.  
  116.             if (_methods.ContainsKey(key))
  117.                 func = _methods[key];
  118.             else
  119.             {
  120.                 var argTypes = args.Select(arg => arg is IObjectWithType ? (arg as IObjectWithType).Type : arg.GetType()).ToArray();
  121.                 IEnumerable<MethodInfo> methods = this.Type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
  122.  
  123.                 methods = from method in methods
  124.                           where method.Name == binder.Name && ArgsMatch(method, argTypes)
  125.                           select method;
  126.  
  127.                 var info = methods.FirstOrDefault();
  128.  
  129.                 if (info != null)
  130.                 {
  131.                     var paramTypes = info.GetParameters().Select(p => p.ParameterType).ToArray();
  132.  
  133.                     var target = Expression.Parameter(this.Type, “obj”);
  134.                     var param = Expression.Parameter(typeof(object[]), “args”);
  135.                     var call = Expression.Call(target, info,
  136.                         paramTypes.Select((p, i) =>
  137.                             BuildConvertExpression(Expression.ArrayAccess(param, Expression.Constant(i)), p)));
  138.  
  139.                     func = Expression.Lambda<Func<T, object[], object>>(
  140.                         Expression.Convert(call, typeof(object)), target, param).Compile();
  141.  
  142.  
  143.                 }
  144.  
  145.                 _methods.Add(key, func);
  146.             }
  147.  
  148.             if (func != null)
  149.             {
  150.                 var res = func(this.Object, args);
  151.  
  152.                 result = Wrap(res);
  153.  
  154.                 return true;
  155.             }
  156.             else
  157.                 return base.TryInvokeMember(binder, args, out result);
  158.         }
  159.  
  160.         public override bool TryConvert(ConvertBinder binder, out object result)
  161.         {
  162.             if (binder.Type.IsAssignableFrom(this.Type))
  163.             {
  164.                 result = this.Object;
  165.                 return true;
  166.             }
  167.             else
  168.                 return base.TryConvert(binder, out result);
  169.         }
  170.  
  171.  
  172.  
  173.         #region Builders
  174.         #region Getter
  175.         private Func<T, object> BuildGetter(MemberInfo member)
  176.         {
  177.             switch (member.MemberType)
  178.             {
  179.                 case MemberTypes.Field:
  180.                     return BuildFieldGetter(member as FieldInfo);
  181.                 case MemberTypes.Property:
  182.                     return BuildPropertyGetter(member as PropertyInfo);
  183.                 default:
  184.                     //Returning null effectively marks this as not supported, since the getter will be null as binding exception will be thrown
  185.                     return null;
  186.             }
  187.         }
  188.  
  189.         private Func<T, object> BuildFieldGetter(FieldInfo fieldInfo)
  190.         {
  191.             var param = Expression.Parameter(this.Type, “obj”);
  192.  
  193.             var lambda = Expression.Lambda<Func<T, object>>(
  194.                 Expression.Field(param, fieldInfo),
  195.                 param);
  196.  
  197.             return lambda.Compile();
  198.         }
  199.  
  200.         private Func<T, object> BuildPropertyGetter(PropertyInfo propertyInfo)
  201.         {
  202.             var param = Expression.Parameter(this.Type, “obj”);
  203.  
  204.             var lambda = Expression.Lambda<Func<T, object>>(
  205.                 Expression.Property(param, propertyInfo),
  206.                 param);
  207.  
  208.             return lambda.Compile();
  209.         }
  210.         #endregion
  211.         #region Setter
  212.         private Action<T, object> BuildSetter(MemberInfo member)
  213.         {
  214.             switch (member.MemberType)
  215.             {
  216.                 case MemberTypes.Field:
  217.                     return BuildFieldSetter(member as FieldInfo);
  218.                 case MemberTypes.Property:
  219.                     return BuildPropertySetter(member as PropertyInfo);
  220.                 default:
  221.                     //Returning null effectively marks this as not supported, since the setter will be null as binding exception will be thrown
  222.                     return null;
  223.             }
  224.         }
  225.  
  226.         private Action<T, object> BuildFieldSetter(FieldInfo fieldInfo)
  227.         {
  228.             var param = Expression.Parameter(this.Type, “obj”);
  229.             var value = Expression.Parameter(typeof(object), “val”);
  230.  
  231.             var lambda = Expression.Lambda<Action<T, object>>(
  232.                 Expression.Assign(
  233.                     Expression.Field(param, fieldInfo),
  234.                     Expression.Convert(value, fieldInfo.FieldType)),
  235.                 param, value);
  236.  
  237.             return lambda.Compile();
  238.         }
  239.  
  240.         private Action<T, object> BuildPropertySetter(PropertyInfo propertyInfo)
  241.         {
  242.             var param = Expression.Parameter(this.Type, “obj”);
  243.             var value = Expression.Parameter(typeof(object), “val”);
  244.  
  245.             var lambda = Expression.Lambda<Action<T, object>>(
  246.                 Expression.Assign(
  247.                     Expression.Property(param, propertyInfo),
  248.                     Expression.Convert(value, propertyInfo.PropertyType)),
  249.                 param, value);
  250.  
  251.             return lambda.Compile();
  252.         }
  253.         #endregion
  254.         #region Convert
  255.         public Expression BuildConvertExpression(Expression target, Type type)
  256.         {
  257.             if (type == typeof(object))
  258.                 return target;
  259.  
  260.             return Expression.Call(_doConvert.MakeGenericMethod(type), target);
  261.         }
  262.  
  263.         static R DoConvert<R>(object i)
  264.         {
  265.             if (i is IConvertTo<R>)
  266.             {
  267.                 return (i as IConvertTo<R>).Convert();
  268.             }
  269.             else
  270.             {
  271.                 return (R)i;
  272.             }
  273.         }
  274.         #endregion
  275.         #endregion
  276.  
  277.         #region Helpers
  278.         private static object Wrap(object res)
  279.         {
  280.             if (res == null)
  281.                 return null;
  282.  
  283.             var type = res.GetType();
  284.  
  285.             if (type.IsPrimitive)
  286.                 return res;
  287.  
  288.             var expType = typeof(Exposer<>).MakeGenericType(type);
  289.  
  290.             return Activator.CreateInstance(expType, res);
  291.         }
  292.  
  293.         private static string MakeKey(InvokeMemberBinder binder, object[] args)
  294.         {
  295.             var ret = new StringBuilder();
  296.             ret.Append(binder.Name);
  297.  
  298.             foreach (var arg in args)
  299.                 ret.Append(arg.GetType().Name);
  300.  
  301.             return ret.ToString();
  302.         }
  303.  
  304.         private static bool ArgsMatch(MethodInfo info, Type[] argTypes)
  305.         {
  306.             return info.GetParameters()
  307.                 .Select((p, i) => p.ParameterType.IsAssignableFrom(argTypes[i]))
  308.                 .All(b => b);
  309.         }
  310.         #endregion
  311.  
  312.         #region IConvertTo<T> Members
  313.  
  314.         public T Convert()
  315.         {
  316.             return this.Object;
  317.         }
  318.  
  319.         #endregion
  320.     }
  321.  
  322.     public static class Extensions
  323.     {
  324.         public static Exposer<T> Expose<T>(this T target)
  325.         {
  326.             return new Exposer<T>(target);
  327.         }
  328.     }
  329. }

C# 4.0: dynamic’s compiler tricks

So, I was curious today, I had some code that used dynamic and was wondering what the heck the compiler was doing on the back end. So, I wrote up a very small test program, and tore into it using Reflector. The version of Reflector I used still only decompiles to C# 3, so it happily displayed all of the inner workings to me without trying to hide any of it. Here is my test program:

static void Main(string[] args)
{
    dynamic foo = "bar";
    int meep = foo.Length;
}

Nice and simple, it shouldn’t look too bad… Well, the compiler generates a static container class that it uses to keep track of some things, and it names everything using names that can’t possibly collide with the programmer’s code. So, instead of showing you code that could make your eyes bleed, I took the liberty of renaming everything and inserting new lines to help readability, the result was:

static class CallSiteContainer
{
    public static CallSite<Func<CallSite, object, int>> convertSite { get; set; }
    public static CallSite<Func<CallSite, object, object>> getLengthSite { get; set; }
}

private static void Main(string[] args)
{
    object foo = "bar";

    if (CallSiteContainer.convertSite == null)
        CallSiteContainer.convertSite = CallSite<Func<CallSite, object, int>>
            .Create(new CSharpConvertBinder(typeof(int), CSharpConversionKind.ImplicitConversion, false));

    if (CallSiteContainer.getLengthSite == null)
        CallSiteContainer.getLengthSite = CallSite<Func<CallSite, object, object>>
            .Create(new CSharpGetMemberBinder("Length", typeof(Program),
                new CSharpArgumentInfo[] {
                    new CSharpArgumentInfo(CSharpArgumentInfoFlags.None, null)
                }));

    int meep = CallSiteContainer.convertSite.Target.Invoke(
        CallSiteContainer.convertSite,
        CallSiteContainer.getLengthSite.Target.Invoke(
            CallSiteContainer.getLengthSite, foo));
}

So, the first thing to notice is that the compiler is generating a static inner container class that holds the call sites for the operation so that it doesn’t have to keep creating them over and over. A call site is a point where we interact with a dynamic object, we have two here, the get on .Length and the conversion of the result of that get to an integer. Also notice that foo is actually just an object when things are all said and done, the compiler just wraps all interactions with it.

The generic parameter on the CallSite class ends up determining the signature of the call site’s Invoke method. Since the CallSiteContainer.convertSite ends up being the equivalent of the folling lambda, it takes Func<CallSite, object, int> to say that it takes a CallSite, and the object to convert as parameters, then returns an integer.

Func<object, int> convertSite = obj => (int)obj;

It just takes whatever object and tries to convert it to an int. The other call site is a bit trickier, because you cannot express it as a lambda at compile time, but you could generate one at runtime, look here for details on that. What we do know is that the CallSiteContainer.getLengthSite fetches the value of the Length property or field off of whatever object it is handed. It’s not yet clear to me why the CSharpGetMemberBinder takes the Program type and that CSharpArgumentInfo array, but it probably has something to do with how the DLR caches the code it generates. This whole thing is powered by the DLR, which does all the code generation and caching transparently on the back end. So be thankful for IronPython being around to kick all of this stuff off ;).

When we want to actually perform our operation, getting the Length of foo, and assigning it to meep, it just Invokes both of the call sites, passing the output of the getLengthSite to the convertSite, then taking the result of that and giving it to meep. I don’t know why each call site is passed itself as the first parameter, but after that comes what would be passed into the lambda equivalent of each call site. When the call site is Invoked, it is passed in so it can be updated to handle new cases that it hasn’t seen before, then all of the arguments that would have been passed into the lambda equivalent follow.

Well, that’s about it, all of this generated code isn’t too complex, it’s just pretty wordy. Looking at this and DynamicObject, it occurs to me that implementing Ruby style Mixins, methodMissing, and send shouldn’t be too hard. If you feel like I left anything out, please point it out in the comments.

EDIT: Incorporated information that Curt provided in the comments.

C# 4.0: DXLinq, a DynamicObject example

System.Xml.Linq is a great library, it lets us very easily manipulate XML, in very close fashion to how we would interact with other data sources. My only issue is that it tends to seem a little redundant,  given this XML:

<A>
  <B X="1"/>
  <B X="2"/>
</A>

We would do the following to get the value of both the X attributes:

var root = XElement.Load("Sample.xml");

foreach (var b in root.Elements("B"))
    Console.WriteLine(b.Attribute("X").Value);

That’s not exactly the cleanest code ever, so what if we could do this instead:

var root = XElement.Load("Sample.xml");

dynamic xml = new DXElement(root);

foreach (var b in xml.B)
    Console.WriteLine(b.X);

Using the combination of C# 4.0’s new dynamic type, and the DynamicObject class, getting this working is actually pretty easy. What we need is a dynamic that will map properties to child elements and attributes. The DynamicObject base class provides an overloadable TryGetMember method that is called whenever the code tries to get a property off of a dynamic. For example, when xml.B is used above, DynamicObject.TryGetMember is called, asking the DynamicObject to return the value of the member. So the DXElement gets to decide what type and value to return, at runtime. Here is my DXElement class, in its entirety:

public class DXElement : DynamicObject
{
    public XElement BaseElement { get; set; }

    public DXElement(XElement baseElement)
    {
        this.BaseElement = baseElement;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        //Go ahead and try to fetch all of the elements matching the member name, and wrap them
        var elements = BaseElement.Elements(binder.Name).Select(element => new DXElement(element));

        //Get the count now, so we don't have to call it twice
        int count = elements.Count();
        if (count > 0)
        {
            if (count > 1)
                result = elements; //We have more than one matching element, so let's return the collection
            else
                result = elements.FirstOrDefault(); //There is only one matching element, so let's just return it

            return true; //return true cuz we matched
        }
        else
        {
            //Ok, so no elements matched, so lets try attributes
            var attributes = BaseElement.Attributes(binder.Name).Select(attr => attr.Value);
            count = attributes.Count();

            if (count > 0)
            {
                if (count > 1)
                    result = attributes; //more than one attribute matched, lets return the collection
                else
                    result = attributes.FirstOrDefault(); //only one attribute matched, lets just return it

                return true; // return true cuz we matched
            }
        }

        //no matches, let's let the base class handle this
        return base.TryGetMember(binder, out result);
    }
}

Not too bad right? Now, it makes a couple of assumptions about the input XML, which are most likely false, but for examples sake, we’ll just ignore that fact ;). As you can see, there isn’t much code needed, just the TryGetMemeber overload using XLinq against the BaseElement to find what we want to return as the result.