Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use collection initializer syntax with ExpandoObject?

I've noticed that the new ExpandoObject implements IDictionary<string,object> which has the requisite IEnumerable<KeyValuePair<string, object>> and Add(string, object) methods and so it should be possible to use the collection initialiser syntax to add properties to the expando object in the same way as you add items to a dictionary.

Dictionary<string,object> dict = new Dictionary<string,object>() 
{
    { "Hello", "World" }
};

dynamic obj = new ExpandoObject()
{
    { "foo", "hello" },
    { "bar", 42 },
    { "baz", new object() }
};

int value = obj.bar;

But there doesn't seem to be a way of doing that. Error:

'System.Dynamic.ExpandoObject' does not contain a definition for 'Add'

I assume this doesn't work because the interface is implemented explicitly. but is there any way of getting around that? This works fine,

IDictionary<string, object> exdict = new ExpandoObject() as IDictionary<string, object>();
exdict.Add("foo", "hello");
exdict.Add("bar", 42);
exdict.Add("baz", new object());

but the collection initializer syntax is much neater.

like image 268
eddwo Avatar asked May 06 '11 10:05

eddwo


People also ask

How to use collection initializer in c#?

Collection initializers let you specify one or more element initializers when you initialize a collection type that implements IEnumerable and has Add with the appropriate signature as an instance method or an extension method. The element initializers can be a simple value, an expression, or an object initializer.

How to initialize object in c#?

The compiler processes object initializers by first accessing the parameterless instance constructor and then processing the member initializations. Therefore, if the parameterless constructor is declared as private in the class, object initializers that require public access will fail.

When to use ExpandoObject?

Use ExpandoObject to create an object that you can add properties, methods, and events to and be able to data bind to in a user interface. ExpandoObject allows you to write code that is more readable than typical reflection code with GetProperty(“Field”) syntax.

How do I know if I have ExpandoObject property?

For ExpandoObject, you can simply check whether the property is defined as a key in the underlying dictionary. For other implementations, it might be challenging and sometimes the only way is to work with exceptions.


3 Answers

I've had the need for a simple ExpandoObject initializer several times before and typically use the following two extension methods to accomplish something like initializer syntax:

public static KeyValuePair<string, object> WithValue(this string key, object value)
{
    return new KeyValuePair<string, object>(key, value);
}

public static ExpandoObject Init(
    this ExpandoObject expando, params KeyValuePair<string, object>[] values)
{
    foreach(KeyValuePair<string, object> kvp in values)
    {
        ((IDictionary<string, Object>)expando)[kvp.Key] = kvp.Value;
    }
    return expando;
}

Then you can write the following:

dynamic foo = new ExpandoObject().Init(
    "A".WithValue(true),
    "B".WithValue("Bar"));

In general I've found that having an extension method to build KeyValuePair<string, object> instances from a string key comes in handy. You can obviously change the name to something like Is so that you can write "Key".Is("Value") if you need the syntax to be even more terse.

like image 133
daveaglick Avatar answered Oct 05 '22 10:10

daveaglick


The language specification (7.5.10.3 on Collection Initializers) is a bit vague on this point as far as I can tell. It says

For each specified element in order, the collection initializer invokes an Add method on the target object with the expression list of the element initializer as argument list, applying normal overload resolution for each invocation. Thus, the collection object must contain an applicable Add method for each element initializer.

Unfortunately the text doesn't go into details about what an applicable Add method is, but it seems that explicitly implemented interface methods don't fit the bill as they are essentially considered private (see 13.4.1):

It is not possible to access an explicit interface member implementation through its fully qualified name in a method invocation, property access, or indexer access. An explicit interface member implementation can only be accessed through an interface instance, and is in that case referenced simply by its member name.

...

Explicit interface member implementations have different accessibility characteristics than other members. Because explicit interface member implementations are never accessible through their fully qualified name in a method invocation or a property access, they are in a sense private. However, since they can be accessed through an interface instance, they are in a sense also public.

like image 37
Brian Rasmussen Avatar answered Oct 05 '22 10:10

Brian Rasmussen


The opensource framework Dynamitey has an alternative syntax for building ExpandoObject instances inline.

    dynamic obj = Builder.New<ExpandoObject>(
        foo:"hello",
        bar: 42 ,
        baz: new object()
    );

    int value = obj.bar;

It also has a dictionary based dynamic prototype object Dynamitey.DynamicObjects.Dictionary such that

    dynamic obj = new Dynamitey.DynamicObjects.Dictionary()
    {
        { "foo", "hello" },
        { "bar", 42 },
        { "baz", new object() }
    };

    int value = obj.bar;

works too.

like image 24
jbtule Avatar answered Oct 05 '22 09:10

jbtule