Динамическое добавление членов к динамическому объекту

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

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

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

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

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

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

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

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


person Thomas Levesque    schedule 17.01.2010    source источник
comment
В C # 6.0 может быть, вы можете написать это как foo.$Bar = 42; :) Не уверен, что это разрешено для динамических ...   -  person nawfal    schedule 21.07.2014
comment
@nawfal, на самом деле, эта функция была удалена ... но в любом случае foo.$Bar - это просто сокращение для foo["Bar"]   -  person Thomas Levesque    schedule 21.07.2014
comment
Томас, не знал, что функция удаляется (я рад этому), но о да, на мгновение я упустил из виду настоящие требования вашего q.   -  person nawfal    schedule 21.07.2014


Ответы (4)


То, что вы хотите, похоже на функции Python getattr / setattr. Нет встроенного эквивалентного способа сделать это в 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);
person Dino Viehland    schedule 17.01.2010
comment
Мне нужно время, чтобы убедиться, что я действительно понимаю, что делает этот код, но в любом случае он работает нормально ... Спасибо! - person Thomas Levesque; 17.01.2010
comment
Можно ли сделать это ‹.Net 4.0 с помощью DLR? Похоже, что использование динамики исключило бы это. - person Firestrand; 03.02.2010
comment
Приведенный выше метод не должен работать должным образом. Сайт вызова установщика требует предоставления 2 аргументов информации вместо 1. Правильным определением будет: new [] {CSharpArgumentInfo.Create (CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create (CSharpArgumentInfoFlags.None, null)} - person Denis Vuyka; 12.05.2011

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

dynamic foo = new ExpandoObject();
foo.Bar = 42;
food = (IDictionary<string,object>)foo;
food["Baz"] = 54
person Uffe Seerup    schedule 12.08.2011
comment
Спасибо! Действительно, ExpandoObject реализует IDictionary, как я понял впоследствии, так что это, безусловно, самое простое решение в этом случае. Однако круг вопросов был шире: я искал решение, которое работало бы с любым динамическим объектом, а не только с ExpandoObject. - person Thomas Levesque; 14.08.2011

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

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

Он также может вызывать многие другие виды связывателей C #.

person jbtule    schedule 06.07.2011

Я знаю, что это довольно старый пост, но я подумал, что пропущу Решение Мирона Абрамсона о том, как вы можете создать свой собственный тип и добавлять свойства во время выполнения - на случай, если кто-то еще там ищу нечто подобное.

person JoBot    schedule 28.03.2012