0

So I'm using an ExpandoObject to add properties dynamically populated into a string. I am going to use these to bind to a DataGrid later. Given below are 2 variations of the code, which look like they are doing the same thing (to me atleast) but one fails and one doesn't. Can someone help me understand why this is?

Failed Code

    dynamic dynamo = new ExpandoObject() as IDictionary<string,object>;
    string words[] = basestring.Split('|');
    foreach(string word in words)
   {
        dynamo[word] = word.ToUpperInvariant();
   }

Successful Code

 dynamic dynamo = new ExpandoObject();
 var dynamoose = dynamo as as IDictionary<string,object>; //Notice the cast
 string words[] = basestring.Split('|');
 foreach(string word in words)
{
   dynamoose[word] = word.ToUpperInvariant();
}

`

Suhas Anjaria
  • 120
  • 1
  • 11

2 Answers2

0

Your code doesn't run directly. I couldn't get the double as cast to work. And your string words[] didn't compile, so I changed it to string[] words.

From there on the failing code gave me this valuable info:

Additional information: Cannot apply indexing with [] to an expression of type 'System.Dynamic.ExpandoObject'

Your second piece of code works, because you have an instance of IDictionary while the failing piece tries to call []-indexing operator on dynamic.

Peheje
  • 12,542
  • 1
  • 21
  • 30
0

This is not a real answer, but I try to show what the compiler does. So try out an easier version of your program:

private static void Main(string[] args)
{
    Method1();
    Method2();

}

private static void Method1()
{
    var expando = new ExpandoObject() as IDictionary<string, object>;
    expando["key"] = 1;
}

private static void Method2()
{
    dynamic expando = new ExpandoObject() as IDictionary<string, object>;
    expando["key"] = 1;
}

Using ILSpy you can see, what the compiler creates, even before any runtime is used:

[CompilerGenerated]
private static class <Method2>o__SiteContainer0
{
    public static CallSite<Func<CallSite, object, string, int, object>> <>p__Site1;
}
private static void Main(string[] args)
{
    Program.Method1();
    Program.Method2();
}
private static void Method1()
{
    IDictionary<string, object> expando = new ExpandoObject();
    expando["key"] = 1;
}
private static void Method2()
{
    object expando = new ExpandoObject();
    if (Program.<Method2>o__SiteContainer0.<>p__Site1 == null)
    {
        Program.<Method2>o__SiteContainer0.<>p__Site1 = CallSite<Func<CallSite, object, string, int, object>>.Create(Binder.SetIndex(CSharpBinderFlags.None, typeof(Program), new CSharpArgumentInfo[]
        {
            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),
            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType | CSharpArgumentInfoFlags.Constant, null),
            CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.UseCompileTimeType | CSharpArgumentInfoFlags.Constant, null)
        }));
    }
    Program.<Method2>o__SiteContainer0.<>p__Site1.Target(Program.<Method2>o__SiteContainer0.<>p__Site1, expando, "key", 1);
}

Method1 should be very clear, since the compiler does only change the var to IDictionary and uses expando as expected.

In Method2 here the compiler does the "magic". As stated in this answer IDictionary is an explicit interface, which cannot accessed through the class instance.

If you cast the dynamic to IDictionary, the compiler creates a very different code:

private static void Method3()
{
    dynamic expando = new ExpandoObject();
    ((IDictionary<string, object>)expando)["key"] = 1;
}

will become

[CompilerGenerated]
private static class <Method3>o__SiteContainer0
{
    public static CallSite<Func<CallSite, object, IDictionary<string, object>>> <>p__Site1;
}

private static void Method3()
{
    object expando = new ExpandoObject();
    if (Program.<Method3>o__SiteContainer0.<>p__Site1 == null)
    {
        Program.<Method3>o__SiteContainer0.<>p__Site1 = CallSite<Func<CallSite, object, IDictionary<string, object>>>.Create(Binder.Convert(CSharpBinderFlags.ConvertExplicit, typeof(IDictionary<string, object>), typeof(Program)));
    }
    Program.<Method3>o__SiteContainer0.<>p__Site1.Target(Program.<Method3>o__SiteContainer0.<>p__Site1, expando)["key"] = 1;
}

As you can see, the CallSite now uses the type IDictionary.

Community
  • 1
  • 1
Matt
  • 4,612
  • 1
  • 24
  • 44
  • But doesn't the ExpandoObject implement the IDictionary, IEnumerable and all that god stuff? I thought since the object instance of type dynamic has been set to be of a class that implements certain interfaces, the compiler recognises them at Runtime? – Suhas Anjaria Jul 09 '15 at 04:07
  • I changed the answer. Actually I am a bit confused also, so I don't explain, just show some output the compiler does. Also the compiler is not executed anymore after compiling, so there is not possibility for it to recognize at runtime. – Matt Jul 09 '15 at 09:16