Why can't I index into an ExpandoObject?
Asked Answered
B

2

61

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?

Becki answered 6/11, 2014 at 11:38 Comment(2)
Explicit interface implementations can only be accessed if the object is cast to that interface, it is not something particular to ExpanoObject.Shoshone
Relevant source code: referencesource.microsoft.com/#System.Core/Microsoft/Scripting/…Acetic
E
82

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"]
Eldwun answered 6/11, 2014 at 11:41 Comment(5)
Thanks @CodeCaster. I knew about explicit interface implementation, but I thought it was just for implementing two members with the same name; I'd never seen it used merely to hide a member before. Do you know why they've done this with ExpandoObject?Becki
@Becki one can only guess, but see Marc Gravell's answer on a similar question: "to keep the core public API clean".Eldwun
This seems to defeat the purpose of even using an ExpandoObject. May as well just declare it as a Dictionary.Ceremonious
@Ceremonious they created ExpandoObject for the ASP.NET MVC viewbag, it's one of the most useless types anywhere else.Panicle
@Ceremonious I'm surprised that your comment has this many likes. ExpandoObject in this context can be used both as dynamic and Dictionary you can assign a field dynamically exp.MyField = 3 and read it as a dictionary value ((IDictionary<String, Object>)exp)["MyField"] or vice versa. There is a situation in my project which can only be solved using ExpandoObjects.Ea
A
4

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;
    }
}
Abreact answered 14/6, 2020 at 19:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.