Easy reflection using a DynamicObject

 
 
  • Gérald Barré

A while back, I received a question on Twitter from Sturla Þorvaldsson about testing private methods. In older versions of Visual Studio, you could generate a wrapper to access private methods in your test projects, but newer versions no longer include this feature. While some argue you should not test private methods directly, it is sometimes more practical to do so rather than going through public methods.

source: Twitter

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:

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

If you only need to access one member, this is fine. However, accessing many members this way quickly becomes verbose. The dynamic type, introduced in .NET 4, allows us to simplify these calls. With dynamic, you can write code that is resolved at runtime rather than at compile time.

C#
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 transparently mix static and dynamic typing:

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

The most powerful feature of dynamic types comes from the IDynamicMetaObjectProvider interface and DynamicObject, which implements it. Using this class, you can easily define the dynamic behavior of your object, such as getting or setting a member value, invoking a method, or performing a type conversion.

C#
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:

C#
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:

C#
[Test]
public void Test()
{
    dynamic test = new ReflectionDynamicObject(new Sample());
    test.MyMethod();
    Assert.AreEquals(10, test._privateField);
}

Do you have a question or a suggestion about this post? Contact me!

Follow me:
Enjoy this blog?