Каковы истинные преимущества ExpandoObject?


587

Класс ExpandoObject , добавляемый в .NET 4, позволяет произвольно устанавливать свойства объекта во время выполнения.

Есть ли какие-либо преимущества перед использованием Dictionary<string, object>или даже Hashtable ? Насколько я могу судить, это не что иное, как хеш-таблица, к которой вы можете получить доступ с немного более лаконичным синтаксисом.

Например, почему это так:

dynamic obj = new ExpandoObject();
obj.MyInt = 3;
obj.MyString = "Foo";
Console.WriteLine(obj.MyString);

Действительно лучше или существенно отличается, чем:

var obj = new Dictionary<string, object>();
obj["MyInt"] = 3;
obj["MyString"] = "Foo";

Console.WriteLine(obj["MyString"]);

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

Ответы:


689

Так как я написал статью MSDN, на которую вы ссылаетесь, думаю, я должен ответить на нее.

Во-первых, я ожидал этого вопроса, и поэтому я написал сообщение в блоге, в котором показан более или менее реальный вариант использования ExpandoObject: Dynamic в C # 4.0: введение в ExpandoObject .

Вкратце, ExpandoObject может помочь вам создавать сложные иерархические объекты. Например, представьте, что у вас есть словарь в словаре:

Dictionary<String, object> dict = new Dictionary<string, object>();
Dictionary<String, object> address = new Dictionary<string,object>();
dict["Address"] = address;
address["State"] = "WA";
Console.WriteLine(((Dictionary<string,object>)dict["Address"])["State"]);

Чем глубже иерархия, тем страшнее код. С ExpandoObject он остается элегантным и читабельным.

dynamic expando = new ExpandoObject();
expando.Address = new ExpandoObject();
expando.Address.State = "WA";
Console.WriteLine(expando.Address.State);

Во-вторых, как уже указывалось, ExpandoObject реализует интерфейс INotifyPropertyChanged, который дает вам больший контроль над свойствами, чем словарь.

Наконец, вы можете добавить события в ExpandoObject, как здесь:

class Program
{
   static void Main(string[] args)
   {
       dynamic d = new ExpandoObject();

       // Initialize the event to null (meaning no handlers)
       d.MyEvent = null;

       // Add some handlers
       d.MyEvent += new EventHandler(OnMyEvent);
       d.MyEvent += new EventHandler(OnMyEvent2);

       // Fire the event
       EventHandler e = d.MyEvent;

       e?.Invoke(d, new EventArgs());
   }

   static void OnMyEvent(object sender, EventArgs e)
   {
       Console.WriteLine("OnMyEvent fired by: {0}", sender);
   }

   static void OnMyEvent2(object sender, EventArgs e)
   {
       Console.WriteLine("OnMyEvent2 fired by: {0}", sender);
   }
}

Также имейте в виду, что ничто не мешает вам динамически принимать аргументы события. Другими словами, вместо использования EventHandlerвы можете использовать, EventHandler<dynamic>что приведет к появлению второго аргумента обработчика dynamic.


53
Интересно. Спасибо за информацию о событиях. Это было новым для меня.
Рид Копси

16
@AlexandraRusina, откуда он знает, что это событие, когда ты говоришь d.MyEvent = null;, или нет?
Шимми Вайцхандлер

20
Может быть, я что-то упускаю, но это не событие - это простое свойство типа делегата.
Сергей Березовский

7
Первый блок может быть написан с использованием анонимных типов: var expando = new { Address = new { State = "WA" } }; Console.WriteLine(expando.Address.State);я нахожу это более читабельным, но ymmv. И учитывая, что он статически типизирован, он более полезен в этом контексте.
Nawfal

13
@nawfal это не правильно - аноним отличается от Expando. Вы создаете анонимный тип, который затем не может добавлять произвольные свойства.
Доктор Blowhard

75

Одно преимущество - для обязательных сценариев. Сетки данных и сетки свойств будут подбирать динамические свойства через систему TypeDescriptor. Кроме того, привязка данных WPF будет понимать динамические свойства, поэтому элементы управления WPF могут связываться с ExpandoObject более легко, чем словарь.

Совместимость с динамическими языками, которые будут ожидать свойства DLR, а не словарные записи, также может рассматриваться в некоторых сценариях.


6
Это кажется , что привязка данных к динамическим объектам нарушается . Пользователь-докладчик eisenbergeffect находится здесь, на SO, и является координатором caliburn.micro. @AlexandraRusina Можете ли вы прокомментировать состояние ошибки и статус «Не исправлю»
surfmuggle

2
Для тех, кому интересно, я в настоящее время могу связываться List<dynamic>и IEnumerable<dynamic>использовать WPF4
Грэм Басс

47

Для меня реальная выгода - это простое связывание данных из XAML:

public dynamic SomeData { get; set; }

...

SomeData.WhatEver = "Yo Man!";

...

 <TextBlock Text="{Binding SomeData.WhatEver}" />

28

Взаимодействие с другими языками, основанными на DLR1-й причине, о которой я могу думать. Вы не можете передать их, Dictionary<string, object>поскольку это не IDynamicMetaObjectProvider. Еще одним дополнительным преимуществом является то, что он реализует, INotifyPropertyChangedчто означает, что в мире привязки данных WPF он также имеет дополнительные преимущества помимо того, что Dictionary<K,V>может предоставить вам.


19

Все дело в удобстве программиста. Я могу представить написание быстрых и грязных программ с этим объектом.


9
@J. Хендрикс, не забывай, что он тоже сказал "грязный". У Intellisense есть свои недостатки, однако, это облегчает отладку и поиск ошибок. Лично я все еще предпочитаю статические, а не динамические типы, если я не сталкиваюсь со странным (и всегда редким) случаем.
Фил

+1 для удобства. Однако я считаю, что анонимные типы могут быть одинаково удобны, как простой пакет свойств, и просто лучше по своей статичности.
Nawfal

1
Я бы не хотел использовать его в рабочем коде, но он очень удобен в тестовом коде и может сделать его очень красивым.
Тобиас

14

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

Это и взаимодействие с динамическими языками, я думаю.


11

Это пример из отличной статьи MSDN об использовании ExpandoObject для создания динамических специальных типов для входящих структурированных данных (например, XML, Json).

Мы также можем назначить делегата динамическому свойству ExpandoObject :

dynamic person = new ExpandoObject();
person.FirstName = "Dino";
person.LastName = "Esposito";

person.GetFullName = (Func<String>)(() => { 
  return String.Format("{0}, {1}", 
    person.LastName, person.FirstName); 
});

var name = person.GetFullName();
Console.WriteLine(name);

Таким образом, это позволяет нам вводить некоторую логику в динамический объект во время выполнения. Следовательно, вместе с лямбда-выражениями, замыканиями, динамическим ключевым словом и классом DynamicObject мы можем ввести некоторые элементы функционального программирования в наш код C #, которые мы знаем по динамическим языкам как JavaScript или PHP.


4

В некоторых случаях это удобно. Я буду использовать его, например, для модульной оболочки. Каждый модуль определяет свой собственный диалог конфигурации, связанный с его настройками. Я предоставляю ему ExpandoObject в качестве Datacontext и сохраняю значения в моей конфигурации Storage. Таким образом, средство записи диалогового окна конфигурации просто привязывается к значению, и оно автоматически создается и сохраняется. (И предоставлены модулю для использования этих настроек, конечно)

Это просто проще в использовании, чем словарь. Но каждый должен знать, что внутренне это просто словарь.

Это похоже на LINQ, только синтаксический сахар, но иногда это облегчает ситуацию.

Поэтому, чтобы ответить на ваш вопрос напрямую: легче писать и легче читать. Но технически это, по сути, является Dictionary<string,object>(Вы можете даже привести его к одному, чтобы перечислить значения).


-1
var obj = new Dictionary<string, object>;
...
Console.WriteLine(obj["MyString"]);

Я думаю, что это работает только потому, что у всего есть ToString (), в противном случае вам нужно будет знать, какой это был тип, и привести «объект» к этому типу.


Некоторые из них полезны чаще, чем другие, я пытаюсь быть тщательным.

  1. Может быть гораздо более естественным получить доступ к коллекции, в данном случае к «словарю», используя более прямую точечную запись.

  2. Кажется, что это можно использовать как действительно хороший Tuple. Вы по-прежнему можете называть своих членов "Item1", "Item2" и т. Д. ... но теперь вам не нужно, это также изменчиво, в отличие от Tuple. Это имеет огромный недостаток отсутствия поддержки intellisense.

  3. Вам может быть неудобно с «именами элементов в виде строк», как и со словарем, вы можете чувствовать, что это слишком похоже на «выполнение строк», и это может привести к кодированию соглашений об именах и работе с морфемами и слоги, когда код пытается понять, как использовать члены :-P

  4. Можете ли вы присвоить значение самому ExpandoObject или только его членам? Сравните и сопоставьте с динамическим / динамическим [], используйте тот, который больше соответствует вашим потребностям.

  5. Я не думаю, что dynamic / dynamic [] работает в цикле foreach, вы должны использовать var, но, возможно, вы можете использовать ExpandoObject.

  6. Вы не можете использовать динамический как элемент данных в классе, возможно, потому что это по крайней мере похоже на ключевое слово, возможно, вы можете использовать с ExpandoObject.

  7. Я ожидаю, что это «ExpandoObject», может быть полезно для маркировки очень общих вещей, с кодом, который различается на основе типов, где используется много динамических вещей.


Будет хорошо, если вы сможете развернуть несколько уровней одновременно.

var e = new ExpandoObject();
e.position.x = 5;
etc...

Это не лучший пример, представьте себе элегантное использование в ваших собственных проектах.

Жаль, что вы не можете заставить код построить некоторые из них и подтолкнуть результаты к intellisense. Я не уверен, как это будет работать, хотя.

Будьте хороши, если они могут иметь ценность, а также членов.

var fifteen = new ExpandoObject();
fifteen = 15;
fifteen.tens = 1;
fifteen.units = 5;
fifteen.ToString() = "fifteen";
etc...

-3

После valueTuples, что за класс ExpandoObject? это 6 строк кода с ExpandoObject:

dynamic T = new ExpandoObject();
T.x = 1;
T.y = 2;
T.z = new ExpandoObject();
T.z.a = 3;
T.b= 4;

можно записать в одну строку с кортежами:

var T = (x: 1, y: 2, z: (a: 3, b: 4));

кроме того, с синтаксисом кортежа у вас есть строгий вывод типа и поддержка intlisense


1
Ваши примеры не идентичны в том смысле, что со значением tuple вы не можете написать Tc = 5; после окончания определите T. С ExpandoObject вы можете сделать это, потому что он динамический. Ваш пример со значением tuple очень похож на объявление анонимного типа. Например: var T2 = new {x = 1, y = 2, z = new {a = 3, b = 4}};
LxL

Почему мне нужно написать Tc = 5 без определения? ExpandoObject полезен только при работе с COM-объектами, которые не защищены в .net. В противном случае я никогда не использую этот ExpandoObject, потому что он грязный и содержит ошибки как во время разработки, так и во время выполнения.
англ. М.Хамди

1
Как насчет того, чтобы z сначала был назначен (a: 3, b: 4), а затем позже вы бы хотели, чтобы z имел дополнительное свойство c? Можете ли вы сделать это со значением кортежа?
LxL

Поэтому я хочу сказать, что вы не можете сравнивать ExpandoObject со значением tuple, потому что они предназначены для разных целей. Сравнивая свой путь, вы отклонили функциональность, для которой предназначен ExpandoObject, а именно динамическую структуру.
LxL
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.