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

538

Класс 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 вместо использования произвольного типа словаря, если не очевидно, что вы используете тип, который будет определен во время выполнения.

Теги:
c#-4.0
.net-4.0

10 ответов

672
Лучший ответ

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

Во-первых, я предвосхитил этот вопрос и почему я написал сообщение в блоге, которое показывает более или менее реальный прецедент для ExpandoObject: Динамический в С# 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;

       if (e != null)
       {
           e(d, new EventArgs());
       }

       // We could also fire it with...
       //      d.MyEvent(d, new EventArgs());

       // ...if we knew for sure that the event is non-null.
   }

   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);
   }
}
  • 49
    Интересно. Спасибо за информацию о событиях. Это было новым для меня.
  • 4
    @ Reed Да, события еще не задокументированы для ExpandoObject. Этот конкретный пример действительно появился в обсуждении в блоге. Я вероятно добавлю это к документации позже.
Показать ещё 10 комментариев
72

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

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

  • 6
    Кажется, что привязка данных к динамическим объектам нарушена . Пользователь-докладчик eisenbergeffect находится здесь, на SO, и является координатором caliburn.micro. @AlexandraRusina Вы можете прокомментировать состояние ошибки и статус "Не исправлю"
  • 1
    Для тех, кто интересуется, я в настоящее время могу связываться с List<dynamic> и IEnumerable<dynamic> с помощью WPF4
40

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

public dynamic SomeData { get; set; }

...

SomeData.WhatEver = "Yo Man!";

...

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

Взаимодействие с другими языками, основанными на DLR, является причиной № 1, о которой я могу думать. Вы не можете передать им Dictionary<string, object>, поскольку это не IDynamicMetaObjectProvider. Другим дополнительным преимуществом является то, что он реализует INotifyPropertyChanged, что означает, что в мире привязки данных WPF он также добавил преимущества, превышающие то, что Dictionary<K,V> может предоставить вам.

18

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

  • 9
    @J. Хендрикс, не забывай, что он тоже сказал "грязный". У Intellisense есть свои недостатки, однако он облегчает отладку и поиск ошибок. Лично я по-прежнему предпочитаю статические, а не динамические типы, если не имею дело со странным (и всегда редким) случаем.
  • 0
    +1 для удобства. Однако я считаю, что анонимные типы могут быть одинаково удобны, как простой пакет свойств, и просто лучше по своей статичности.
Показать ещё 1 комментарий
13

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

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

9

Пример из большой статьи MSDN об использовании ExpandoObject для создания динамических типов ad-hoc для входящих структурированных данных (например, 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 мы можем ввести некоторые элементы функционального программирования в наш код С#, который мы знает из динамических языков, как JavaScript или PHP.

4

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

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

Это похоже на LINQ просто синтаксический сахар, но иногда это делает вещи проще.

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

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

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


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

  • Возможно, гораздо более естественным является доступ к коллекции, в данном случае, что фактически является "словарем", используя более точную нотацию.

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

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

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

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

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

  • Я ожидаю, что это "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}};
  • 0
    Почему мне нужно написать Tc = 5 без определения? ExpandoObject полезен только при работе с COM-объектами, которые не защищены в .net. В противном случае я никогда не использую этот ExpandoObject, потому что он грязный и содержит ошибки как во время разработки, так и во время выполнения.
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню