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.