Easy reflection using a DynamicObject

I got a question a few times ago on Twitter. Sturla Þorvaldsson was asking about testing private methods. In the previous versions of Visual Studio, you could generate a wrapper to use private methods in your test projects, but the latest versions do not have this features. While some people will tell you that you should not test private methods, it is sometime very convenient to test these methods directly without calling the public methods.

Of course, you can change the method from private to internal and use [assembly: InternalsVisibleTo("...")]. However, if you don't want to bother with that, you can use reflection to access the private members. For instance, you can get the value of a private property using the following code:

PropertyInfo property = typeof(Sample).GetProperty("Name", BindingFlags.Instance | BindingFlags.NonPublic);
var value = (int)property.GetValue(foo);

If you only want to access one member this is fine. However, if you need to access more members, it can be very long to write all mappings. The dynamic type will allow us to simply these calls. This type was introduced with .NET 4. With dynamic you can write code that is checked at runtime, and not at compile time.

class Sample
{
    void A()
    {
        dynamic instance = new Sample();
        instance.Foo(); // Compile, but throw an exception at runtime because Foo does not exist (RuntimeBinderException)
    }
}

You can mix static and dynamic typing in a transparent way:

dynamic instance = new Sample();
string value = instance.Foo(); // implicit conversion to string

The most interesting possibilities with dynamic types come with the IDynamicMetaObjectProvider interface and DynamicObject which implements the interface. Using this class you can easily implement the dynamic behavior of your object such as accessing or assigning a value to a member, calling a method, or performing a type conversion.

public class DynamicObject : IDynamicMetaObjectProvider
{
   public virtual bool TryGetMember(GetMemberBinder binder, out object result);
   public virtual bool TrySetMember(SetMemberBinder binder, object value);
   public virtual bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result);
   public virtual bool TryConvert(ConvertBinder binder, out object result);
   public virtual bool TryInvoke(InvokeBinder binder, object[] args, out object result);
   public virtual bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object result);
   public virtual bool TryUnaryOperation(UnaryOperationBinder binder, out object result);
   public virtual bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result);
   public virtual bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value);
   public virtual IEnumerable<string> GetDynamicMemberNames();
}

In our case, we can implement all the methods by using reflection:

public class ReflectionDynamicObject : DynamicObject
{
    private const BindingFlags InstanceDefaultBindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

    private readonly object _originalObject;
    private readonly IDictionary<string, PropertyInfo> _properties = new Dictionary<string, PropertyInfo>();
    private readonly IDictionary<string, FieldInfo> _fields = new Dictionary<string, FieldInfo>();

    public ReflectionDynamicObject(object obj)
    {
        _originalObject = obj ?? throw new ArgumentNullException(nameof(obj));
        CreateMemberCache();
    }

    private void CreateMemberCache()
    {
        foreach (var propertyInfo in type.GetProperties(InstanceDefaultBindingFlags))
            _properties.Add(propertyInfo.Name, propertyInfo);

        foreach (var fieldInfo in type.GetFields(InstanceDefaultBindingFlags))
            _fields.Add(fieldInfo.Name, fieldInfo);
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        string name = binder.Name;
        if (_properties.TryGetValue(name, out var property))
        {
            property.SetValue(_originalObject, value);
            return true;
        }

        if (_fields.TryGetValue(name, out var field))
        {
            field.SetValue(_originalObject, value);
            return true;
        }

        return false;
    }

    // A complete version of this code is available on GitHub:
    // https://github.com/meziantou/Meziantou.Framework/blob/master/src/Meziantou.Framework/ReflectionDynamicObject.cs
}

You can now use the previous class in your tests:

[Test]
public void Test()
{
    dynamic test = new ReflectionDynamicObject(new Sample());
    test.MyMethod();
    Assert.AreEquals(10, test._privateField);
}
Enjoy this blog? Buy Me A Coffee Donate with PayPal

Leave a reply