I have a situation where I want a dynamic object that can used within generic classes and methods.
I have a generic service that accepts any instance that implements a predefined interface:
public interface IMyInterface
{
bool enabled { get; set; }
}
public class MyGenericService<T>
where T : IMyInterface
{
public void Process(T obj)
{
obj.enabled = true;
}
}
And I have a method to call the service:
public void MyGenericMethod<T>(T instance)
where T : IMyInterface
{
var service = new MyGenericService<T>();
service.Process(instance);
}
Microsoft provides the ExpandoObject
for use as a dynamic object but calling the service with the ExpandoObject
throws an error because the ExpandoObject
class does not implement the required interface IMyInterface
:
[Test]
public void ExpandoObject_CallGenericMethod_Throws()
{
dynamic obj = new ExpandoObject();
Assert
.Throws<Microsoft.CSharp.RuntimeBinder.RuntimeBinderException>(
() => MyGenericMethod(obj));
}
So what do we do? The first thought is to inherit from ExpandoObject
but that can’t be done because it is defined as sealed. There are plenty of other options to be found on the web but the first and most useful that I came across was this one: http://www.abhisheksur.com/2010/07/dynamic-behaviour-on-objects-at-runtime.html
I created my own AbstractDynamicObject
:
public abstract class AbstractDynamicObject : DynamicObject
{
public IDictionary<string, object> Dictionary { get; set; }
public AbstractDynamicObject()
{
this.Dictionary = new Dictionary<string, object>();
}
public int Count { get { return this.Dictionary.Keys.Count; } }
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (this.Dictionary.ContainsKey(binder.Name))
{
result = this.Dictionary[binder.Name];
return true;
}
return base.TryGetMember(binder, out result); //means result = null and return = false
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
if (!this.Dictionary.ContainsKey(binder.Name))
{
this.Dictionary.Add(binder.Name, value);
}
else
{
this.Dictionary[binder.Name] = value;
}
return true;
}
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
if (this.Dictionary.ContainsKey(binder.Name) && this.Dictionary[binder.Name] is Delegate)
{
Delegate del = this.Dictionary[binder.Name] as Delegate;
result = del.DynamicInvoke(args);
return true;
}
return base.TryInvokeMember(binder, args, out result);
}
public override bool TryDeleteMember(DeleteMemberBinder binder)
{
if (this.Dictionary.ContainsKey(binder.Name))
{
this.Dictionary.Remove(binder.Name);
return true;
}
return base.TryDeleteMember(binder);
}
public override IEnumerable<string> GetDynamicMemberNames()
{
foreach (string name in this.Dictionary.Keys)
{
yield return name;
}
}
}
Which I could then extend to implement the interface IMyInterface
and violá everything works!
public class MyDynamicObject : AbstractDynamicObject, IMyInterface
{
public bool enabled { get; set; }
}
[Test]
public void MyDynamicObject_ByDefault_IsNotEnabled()
{
dynamic obj = new MyDynamicObject();
Assert
.That(obj.enabled,
Is.False);
}
[Test]
public void MyDynamicObject_CallGenericMethod_GetsEnabled()
{
dynamic obj = new MyDynamicObject();
MyGenericMethod(obj);
Assert
.That(obj.enabled,
Is.True);
}