ExpandoObject - 为什么类型的行为不同?

ExpandoObject - why a Type behaves differently?

一个给大师,请说服me/us这是怎么回事。

   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...

在所有情况下,LEFT-HAND SIDE 都是 System.Dynamic.ExpandoObject; 那么,为什么在第 4 个案例 expObj4 中,我无法访问 属性 expObj4.first?

这是因为变量 expObj4 被声明为 ExpandoObject 而不是 dynamic。这是一个重要的区别。

试试这个:

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

这个可以编译,但下面的不能:

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

你明白了:

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

您拥有的与此相关的变量是:

  • expandoObj1 - 动态
  • expandoObj2 - 动态
  • expObj1 - 动态
  • expObj2 - 动态
  • expObj3 - 字典,但是你在这里使用字典访问,而不是点访问

编译器的神奇 "let's see if we can access the thing at runtime" 代码仅在表达式或变量为 dynamic 时才会启动。 ExpandoObject只是支持这个的类型

ExpandoObject 是一个密封的 class,它将数据存储在字典中。它实现 IDynamicMetaObjectProvider 接口,该接口为实现它的 classes 提供动态行为。它还实现了 IDictionary 接口,该接口为其提供类似字典的行为。它应该在编译时进行检查和验证。

dynamic 是一种编译器不应在编译时检查的类型。它在运行时被检查和中断。在编译时,假定动态实体支持任何操作。因此,当您说它是一个 expandoobject 时,首先调用的字段不会附加到对象本身。

在这里检查 expando 对象的源代码

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

将动态行为想象成一个对象。你可以把任何类型放在那里。当您添加到列表时,您是以动态方式添加到列表中的,但所添加项目的固有类型是 ExpandoObject。因此,您可以将其转换回 ExpandoObject。

当你说,

expandoObj1.first = "fred";

等于说,

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

当你使用

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

您使用的是动态形式的 expandoObject。因此,您可以直接访问属性。当您将其转换为 ExpandoObject class 时,您使用的是实际的 ExpandoObject class,它在字典中存储字段,因此点 (.) 符号不起作用。

var expObj4 = peopleList[0] as ExpandoObject;

左侧的变量仍然是 ExpandoObject,不是字典。 ExpandoObject 通过集合搜索公开其成员。

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

当您将其转换为字典时,它就像字典一样工作。

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

当您将其转换为动态时,您可以访问这些键,就像它们是 class 的属性一样。

进一步参考 Dynamically adding properties to an ExpandoObject