8

Я ищу способ динамически добавлять участников в динамический объект. Хорошо, я думаю, немного требуется разъяснение ...Динамическое добавление элементов в динамический объект

Когда вы сделаете это:

dynamic foo = new ExpandoObject(); 
foo.Bar = 42; 

Bar свойство будет добавлено динамически во время выполнения. Но код по-прежнему ссылается «статически» на Bar (имя «Bar» жестко закодировано). Что делать, если я хочу добавить свойство во время выполнения, не зная его имени во время компиляции?

Я знаю, как сделать это с помощью пользовательского динамического объекта (я на самом деле blogged about it несколько месяцев назад), используя методы DynamicObject класса, но как я могу сделать это с любого динамического объекта?

Возможно, я использовал интерфейс IDynamicMetaObjectProvider, но я не понимаю, как его использовать. Например, какой аргумент следует передать методу GetMetaObject? (он ожидает Expression)

И, кстати, как вы выполняете отражение на динамических объектах? «Регулярное» отражение и TypeDescriptor не показывают динамические элементы ...

Любое понимание будет оценено!

+0

В C# 6.0, * может быть * вы можете написать его как 'foo. $ Bar = 42;' :) Не уверен, разрешено ли для динамического ... – nawfal

+0

@nawfal, на самом деле эта функция была удалена .. но в любом случае 'foo. $ Bar' является только сокращением для' foo ["Bar"] ' –

+0

Томас, не знал о том, что функция отбрасывается (я рад этому), но о да, на мгновение Я упустил из виду реальное требование вашего q. – nawfal

ответ

9

Что вы хотите, это похоже на функции getattr/setattr Python. В C# или VB.NET нет эквивалентного способа сделать это. Внешний слой DLR (который поставляется с IronPython и IronRuby в Microsoft.Scripting.dll) включает в себя набор API-интерфейсов хостинга, который включает API ObjectOperations с методами GetMember/SetMember. Вы можете использовать их, но вам потребуется дополнительная зависимость от DLR и языка на основе DLR.

Возможно, самым простым подходом было бы создание CallSite с одним из существующих связующих C#. Вы можете получить код для этого, посмотрев на результат «foo.Bar = 42» в ildasm или отражателе. Но простой пример этого был бы:

object x = new ExpandoObject(); 
CallSite<Func<CallSite, object, object, object>> site = CallSite<Func<CallSite, object, object, object>>.Create(
      Binder.SetMember(
       Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags.None, 
       "Foo", 
       null, 
       new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) } 
      ) 
     ); 
site.Target(site, x, 42); 
Console.WriteLine(((dynamic)x).Foo); 
+0

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

+0

Возможно ли это сделать с помощью DLR <.Net 4.0? Похоже, что использование динамического исключает это. – Firestrand

+0

Указанный выше метод не должен работать должным образом. Сеттер сайт вызов требует 2 аргумента информация о быть предусмотрено вместо 1. Правильное определение будет: новый [] { CSharpArgumentInfo.Create (CSharpArgumentInfoFlags.None, нуль), CSharpArgumentInfo.Create (CSharpArgumentInfoFlags.None, нуль) } –

5

Рамки с открытым исходным кодом Dynamitey будет делать это (доступный через nuget). Он инкапсулирует, все еще кэшируя сайт вызова и код связывания, который использовался @ Dino-Viehland.

Dynamic.InvokeSet(foo,"Bar",42); 

Он также может называть множество other kinds of c# binder too.

5

ExpandoObject реализует IDictionary < строка, объект >, хотя и явно. Это означает, что вы можете просто передать объект ExpandoObject в строку IDictionary <, объект > и манипулировать словарем.

dynamic foo = new ExpandoObject(); 
foo.Bar = 42; 
food = (IDictionary<string,object>)foo; 
food["Baz"] = 54 
+0

Спасибо! Действительно, ExpandoObject реализует IDictionary, как я понял впоследствии, поэтому это, безусловно, самое простое решение в этом случае. Однако объем моего вопроса был шире: я искал решение, которое будет работать с любым динамическим объектом, а не только с ExpandoObject –

1

Я знаю, что это довольно старый пост, но я думал, что пройти по Miron Abramson solution о том, как вы можете создать свой собственный тип и добавлять свойства во время выполнения - в случае, если кто-то там что-то ищет аналогичный.