【问题标题】:ExpandoObject quirky behavior while adding items dynamically动态添加项目时 ExpandoObject 的古怪行为
【发布时间】:2015-07-08 21:15:41
【问题描述】:

所以我使用 ExpandoObject 来添加动态填充到字符串中的属性。稍后我将使用这些绑定到 DataGrid。下面给出了代码的 2 种变体,看起来它们正在做同样的事情(至少对我而言),但一种失败,一种没有。有人可以帮我理解这是为什么吗?

失败的代码

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

成功的代码

 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();
}

`

【问题讨论】:

  • 这段代码甚至无法编译。你是怎么尝试的?
  • 我认为as as 是一个错字。
  • 是的,还有单词[]。这可能意味着,他没有从编辑器中复制粘贴。
  • 是的,抱歉,几天前我在我正在处理的 WPF 应用程序上更正了代码,并且懒得启动另一个 CLR 来重写它。我会调查@Matt 的回答并尝试找出原因。
  • 动态编程??

标签: c# dynamic-programming


【解决方案1】:

您的代码不会直接运行。我无法让演员阵容发挥作用。而且你的字符串 words[] 没有编译,所以我把它改成了 string[] words。

从那里失败的代码给了我这个有价值的信息:

附加信息:无法将带有 [] 的索引应用于表达式 'System.Dynamic.ExpandoObject' 类型的

您的第二段代码有效,因为您有一个 IDictionary 实例,而失败的部分尝试动态调用 []-indexing 运算符。

【讨论】:

    【解决方案2】:

    这不是一个真正的答案,但我试图展示编译器的作用。所以尝试一个更简单的程序版本:

    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;
    }
    

    使用ILSpy,您可以看到编译器创建的内容,甚至在使用任何运行时之前:

    [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 应该很清楚,因为编译器只是将 var 更改为 IDictionary 并按预期使用 expando。

    在这里的 Method2 中,编译器执行了“魔术”。如this answerIDictionary 中所述,IDictionary 是一个显式接口,无法通过类实例访问。

    如果将动态转换为 IDictionary,编译器会创建一个非常不同的代码:

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

    会变成

    [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;
    }
    

    如您所见,CallSite 现在使用 IDictionary 类型。

    【讨论】:

    • 但是 ExpandoObject 没有实现 IDictionary、IEnumerable 和所有这些神器吗?我想既然动态类型的对象实例已设置为实现某些接口的类,编译器在运行时识别它们?
    • 我改变了答案。其实我也有点困惑,所以我不解释,只是显示一些编译器的输出。并且编译器在编译后不再执行,因此在运行时无法识别。
    猜你喜欢
    • 2019-08-20
    • 1970-01-01
    • 2011-06-23
    • 1970-01-01
    • 1970-01-01
    • 2011-10-28
    • 2011-05-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多