Question

I have a DynamicObjectand I want it to be castable to IDictionary, exactly the same way as ExpandoObject. For example, casting an ExpandoObject to IDictionary is perfectly valid:

dynamic ex = new ExpandoObject ();
ex.Field = "Foobar";
IDictionary<string, object> dict = ex as IDictionary<string, object>;
Console.WriteLine (dict["Field"]);

Now I try to implement this into my own DynamicObject:

public class MyDynamicObject : DynamicObject
{
    public Dictionary<string, object> members = new Dictionary<string, object> ();

    public override bool TryGetMember (GetMemberBinder binder, out object result)
    {
        if (members.ContainsKey (binder.Name)) {
            result = members[binder.Name];
            return true;
        }
        else {
            result = null;
            return false;
        }
    }
    public override bool TrySetMember (SetMemberBinder binder, object value)
    {
        this.members.Add (binder.Name, value);
        return true;
    }
    public static implicit operator IDictionary<string, object> (MyDynamicObject mydo) 
    {
        return (IDictionary<string, object>) mydo.members;
    }
}

But the compiler will fail on the public static implicit operator IDictionary<string, object>line, giving the error: "Cannot convert to or from an interface type". If I change the implicit operator to Dictionary, I can cast MyDynamicObject to Dictionary without any Problems, but not IDictionary.

How does ExpandoObject does this?

Was it helpful?

Solution

ExpandoObject simply implements IDictionary<string, object>.

If you want to have the same behaviour, you also need to implement this interface and delegate all calls to your members field.

Something like this:

public class MyDynamicObject : DynamicObject, IDictionary<string, object>
{
    public Dictionary<string, object> members = new Dictionary<string, object>();

    public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
    {
        return members.GetEnumerator();
    }

    public void Add(KeyValuePair<string, object> item)
    {
        members.Add(item.Key, item.Value);
    }

    // ...
}

OTHER TIPS

Cast operators are not interrogated by the DLR - therefore this will not work.

You can inherit from IDictionary or you can override the TryConvert method.

Since implementing IDictionary requires either implementing or stubbing out lots of operations, as well, you might find that the TryConvert route is actually easier to implement; the downside is that in your implementation of TryConvert you will almost certainly produce a new instance of a dictionary each time you call it (unless you cache the result and keep it up to date) so it might take an extra performance hit

It's defined in the C# specification:

A class or struct is permitted to declare a conversion from a source type S to a target type T provided all of the following are true:

  • ...
  • Neither S nor T is object or an interface-type.

See this Stack Overflow answer.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top