ExpandoObject — почему тип ведет себя по-другому?

Один для гуру, пожалуйста, убедите меня/нас в том, что происходит.

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

Во всех случаях ЛЕВАЯ СТОРОНА является System.Dynamic.ExpandoObject; Почему тогда в 4-м случае expObj4 я не могу получить доступ к свойству expObj4.first?


person joedotnot    schedule 31.08.2017    source источник
comment
Поскольку переменная не объявлена ​​как dynamic, это важная часть!   -  person Lasse V. Karlsen    schedule 31.08.2017


Ответы (2)


Это связано с тем, что переменная expObj4 объявлена ​​как ExpandoObject, а не как dynamic. Это важное отличие.

Попробуй это:

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

Это компилируется, но следующее нет:

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

вы получаете это:

CS1061 «ExpandoObject» не содержит определения для «Name», и не удалось найти метод расширения «Name», принимающий первый аргумент типа «ExpandoObject».

Переменные, которые у вас есть, которые связаны с этим:

  • expandoObj1 — динамический
  • expandoObj2 — динамический
  • expObj1 — динамический
  • expObj2 — динамический
  • expObj3 - словарь, но здесь вы используете доступ к словарю, а не точечный доступ

Волшебный код компилятора «давайте посмотрим, сможем ли мы получить доступ к этой вещи во время выполнения» срабатывает только в том случае, если выражение или переменная имеют значение dynamic. ExpandoObject — это просто тип, который поддерживает это.

person Lasse V. Karlsen    schedule 31.08.2017

ExpandoObject — это закрытый класс, который хранит данные в словаре. Он реализует интерфейс IDynamicMetaObjectProvider, который обеспечивает динамическое поведение реализующих его классов. Он также реализует интерфейс IDictionary, который обеспечивает поведение, подобное словарю. Предполагается, что он проверяется и проверяется во время компиляции.

dynamic — это тип, который не должен проверяться компилятором во время компиляции. Он проверяется и ломается во время выполнения. Во время компиляции предполагается, что динамический объект поддерживает любую операцию. Итак, когда вы говорите, что это объект Expando, поле, вызываемое первым, не добавляется к самому объекту.

Проверьте исходный код объекта 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, вы использовали фактический класс ExpandoObject, который хранит поля в словаре, поэтому запись через точку (.) не работает.

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"];

Когда вы приводите его к динамике, вы можете получить доступ к этим ключам, как если бы они были свойствами класса.

Дополнительная ссылка Динамическое добавление свойств в ExpandoObject

person Amit Kumar Singh    schedule 31.08.2017
comment
тоже хорошее объяснение, я могу принять только 1 ответ, поэтому проголосовал. - person joedotnot; 01.09.2017