Які справжні переваги 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. Ви створюєте анонімний тип, який не може потім додати довільні властивості.
Д-р Блохард

75

Одна перевага - прив'язка сценаріїв. Сітки даних та сітки властивостей підберуть динамічні властивості через систему TypeDescriptor. Крім того, прив'язка даних WPF зрозуміє динамічні властивості, тому елементи керування WPF можуть прив’язуватися до ExpandoObject легше, ніж до словника.

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


6
Це здається , що прив'язка даних до динамічних об'єктів порушується . Користувач-звітник eisenbergeffect знаходиться тут на SO та координаторі caliburn.micro. @AlexandraRusina Ви можете прокоментувати стан помилки та статус "Не
виправлюсь

2
Для тих, хто цікавиться, на даний момент я можу прив’язатись до WPF4 List<dynamic>та IEnumerable<dynamic>використовувати його
Грехем Бас

47

Справжньою перевагою для мене є абсолютно легке прив'язування даних від XAML:

public dynamic SomeData { get; set; }

...

SomeData.WhatEver = "Yo Man!";

...

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

28

Interop з іншими мовами, заснованими на DLRпринципі №1, я можу придумати. Ви не можете їх передати 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 і зберігаю значення у моїй конфігураційній пам’яті. Таким чином, програма діалогового вікна налаштування просто повинна прив’язати до значення, і вона автоматично створюється та зберігається. (І надається модулю для використання цих параметрів, звичайно)

Це 'просто простіше у використанні, ніж словник. Але всі повинні усвідомлювати, що внутрішньо це лише Словник.

Це як синтаксичний цукор LINQ, але іноді це полегшує справи.

Отже, щоб відповісти на ваше запитання безпосередньо: Простіше писати та легше читати. Але технічно це по суті є Dictionary<string,object>(Ви навіть можете передати його в одне, щоб перерахувати значення).


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

Я думаю, що це працює лише тому, що у всіх є ToString (), інакше вам доведеться знати тип, який він був, і передати "об'єкт" на цей тип.


Деякі з них корисні частіше, ніж інші, я намагаюся бути ретельними.

  1. Доступ до колекції може бути набагато природніше, у цьому випадку це фактично "словник", використовуючи більш прямі позначення крапок.

  2. Схоже, це можна використати як справді приємний кортеж. Ви все ще можете називати своїх членів "Item1", "Item2" і т. Д. ... але тепер цього не потрібно, це також є змінним, на відміну від Tuple. Це має величезний недолік відсутності інтелігенційної підтримки.

  3. Вам може бути незручно з "іменами членів як рядків", як це відчувається зі словником, ви можете відчути, що це занадто схоже на "виконання рядків", і це може призвести до того, що імена конвенцій будуть закодовані, і мати справу з роботою з морфемами та склади, коли код намагається зрозуміти, як користуватися членами :-P

  4. Чи можете ви призначити значення самому ExpandoObject або лише його членам? Порівняйте та порівняйте з динамічним / динамічним [], використовуйте те, що найкраще відповідає вашим потребам.

  5. Я не думаю, що динамічний / динамічний [] працює в циклі foreach, ви повинні використовувати var, але, можливо, ви можете використовувати ExpandoObject.

  6. Ви не можете використовувати динаміку як учасника даних у класі, можливо тому, що це принаймні щось на зразок ключового слова, сподіваємось, що ви можете використовувати ExpandoObject.

  7. Я думаю, що "є" ExpandoObject, може бути корисним позначення дуже універсальних речей, кодом, який диференціюється на основі типів, де використовується багато динамічних речей.


Будь приємним, якщо ти зможеш опустити кілька рівнів одночасно.

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

Це не найкращий можливий приклад, уявіть собі, як доречно, елегантне використання у власних проектах.

Прикро, що ви не можете створити деякі з них і підштовхнути результати до інтелігенції. Я не впевнений, як це буде працювати.

Будьте приємні, якби вони могли мати цінність, а також члени.

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
Ваші приклади не однакові в тому сенсі, що зі значенням кортежу ви не можете записати Tc = 5; закінчивши визначити T. За допомогою ExpandoObject ви можете це зробити, оскільки це динамічно. Ваш приклад зі значенням кортежу дуже ідентичний оголошенню анонімного типу. Напр .: 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 зі значенням кортежу, оскільки вони розроблені для різних цілей. Порівнюючи свій спосіб, ви відхилили функціонал, для якого розроблений ExpandoObject, який є динамічною структурою.
LxL
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.