ExpandoObject - why a Type behaves differently?

339 views Asked by At

One for the gurus, please convince me/us what is going on.

   List<ExpandoObject> peopleList = new List<ExpandoObject>();

    dynamic expandoObj1 = new ExpandoObject();
    expandoObj1.id = 1;
    expandoObj1.first = "fred";
    expandoObj1.last = "krugger";
    peopleList.Add(expandoObj1);

    dynamic expandoObj2 = new ExpandoObject();
    expandoObj2.id = 2;
    expandoObj2.first = "george";
    expandoObj2.last = "benson";
    peopleList.Add(expandoObj2);

    //test access the props
    var expObj = expandoObj1; 
    var name = expObj.first;

    var expObj2 = peopleList[0] as dynamic;
    var name2 = expObj2.first; 

    IDictionary<string, object> expObj3 = peopleList[0] as ExpandoObject;
    var name3 = expObj3["first"];

    var expObj4 = peopleList[0] as ExpandoObject;
    //var name4 = expObj4.first; //THIS DOESN'T WORK - ExpandoObject does not contain a definition for 'first' etc...

In all cases, the LEFT-HAND SIDE is a System.Dynamic.ExpandoObject; Why then, on the 4th case expObj4, i cannot access the property expObj4.first ?

2

There are 2 answers

0
Lasse V. Karlsen On BEST ANSWER

This is because the variable expObj4 is declared as ExpandoObject and not as dynamic. This is an important difference.

Try this:

dynamic a = new ExpandoObject();
a.Name = "Test";

This compiles, but the following doesn't:

ExpandoObject a = new ExpandoObject();
a.Name = "Test";

you get this:

CS1061 'ExpandoObject' does not contain a definition for 'Name' and no extension method 'Name' accepting a first argument of type 'ExpandoObject' could be found

The variables you have that are related to this are:

  • expandoObj1 - dynamic
  • expandoObj2 - dynamic
  • expObj1 - dynamic
  • expObj2 - dynamic
  • expObj3 - dictionary, but you use dictionary access here, not dot-access

The magic "let's see if we can access the thing at runtime" code of the compiler only kicks in if the expression or variable is dynamic. ExpandoObject is just a type that supports this.

1
Amit Kumar Singh On

ExpandoObject is a sealed class which stores data in a dictionary. It implements IDynamicMetaObjectProvider interface which provides dynamic behaviour to the classes implementing it. It also implements IDictionary interface which provides dictionary like behaviour to it. It is supposed to be checked and validated at compile time.

dynamic is a type which is not supposed to be checked by the compiler at compile time. It is checked and breaks at runtime. At compile time, a dynamic entity is assumed to support any operation. So, when you say, it is a expandoobject, the field called first does not get appended to object itself.

Check source code of expando object here

https://github.com/Microsoft/referencesource/blob/master/System.Core/Microsoft/Scripting/Actions/ExpandoObject.cs

Think of dynamic behavior like an object. You can put any type there. When you are adding to list, you are adding to list as dynamic, but the inherent type of item being added is ExpandoObject. So, you are able to cast it back to ExpandoObject.

When you say,

expandoObj1.first = "fred";

it is same as saying,

expandoObj1.Add("first", "fred");

When you used

    var expObj = expandoObj1;
    var name = expObj.first;

you were using expandoObject in dynamic form. So, you were able to access properties directly. When you cast it to ExpandoObject class, you were using actual ExpandoObject class which stores fields in Dictionary, so dot(.) notation does not work.

var expObj4 = peopleList[0] as ExpandoObject;

variable on left hand side is still ExpandoObject, not a dictionary. ExpandoObject exposes its members through collection search.

  var name4 = expObj4.Where(t=>t.Key == "first").First().Value;

When you cast it to a dictionary, it works like a dictionary.

IDictionary<string, object> expObj3 = peopleList[0] as ExpandoObject;
  var name3 = expObj3["first"];

When you cast it to a dynamic, you can access these keys like they are properties of the class.

Further reference Dynamically adding properties to an ExpandoObject