Why can't I index into an ExpandoObject?
Something caught me by surprise when looking into C# dynamics today (I've never used them much, but lately I've been experimenting with the Nancy web framework). I found that I couldn't do this:
dynamic expando = new ExpandoObject();
expando.name = "John";
Console.WriteLine(expando["name"]);
The last line throws an exception:
Cannot apply indexing with [] to an expression of type 'System.Dynamic.ExpandoObject'
I understand the error message, but I don't understand why this is happening. I have looked at the documentation for ExpandoObject and it explicitly implements IDictionary<,>
and thus has a this.[index]
method (MSDN). Why can't I call it?
Of course, there's nothing to stop me from downcasting the ExpandoObject
to a dictionary manually and then indexing into it, but that kind of defies the point; it also doesn't explain how the Expando was able to hide the method of one of its interfaces.
What's going on here?
how the Expando was able to hide the method of one of its interfaces.
Because as you correctly found out in the documentation, the indexer is an explicit interface implementation. From Explicit Interface Implementation Tutorial:
A class that implements an interface can explicitly implement a member of that interface. When a member is explicitly implemented, it cannot be accessed through a class instance, but only through an instance of the interface.
This means you'll have to cast the reference to the interface to access it:
((IDictionary<String, Object>)expando)["name"]
Use this factory class to create ExpandoObjects! Then use HasProperty("prop name") or GetValue("prop name")
void Main()
{
dynamic _obj = ExpandoObjectFactory.Create();
if (_obj.HasProperty("Foo") == false)
{
_obj.Foo = "Foo";
}
Console.WriteLine(_obj); // Foo;
object bar = _obj.GetValue("Bar");
Console.WriteLine(bar); // null
}
public static class ExpandoObjectFactory
{
public static ExpandoObject Create()
{
dynamic expandoObject = new ExpandoObject();
expandoObject.HasProperty = new Func<string, bool>((string name) => ((IDictionary<string, object>)expandoObject).ContainsKey(name));
expandoObject.GetValue = new Func<string, object>(delegate (string name)
{
((IDictionary<string, object>)expandoObject).TryGetValue(name, out object value);
return value;
});
return expandoObject;
}
}