Динамическое добавление членов к динамическому объекту - PullRequest
8 голосов
/ 17 января 2010

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

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

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

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

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

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

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

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

Ответы [ 4 ]

9 голосов
/ 17 января 2010

То, что вы хотите, похоже на функции 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);
5 голосов
/ 13 августа 2011

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

dynamic foo = new ExpandoObject();
foo.Bar = 42;
food = (IDictionary<string,object>)foo;
food["Baz"] = 54
5 голосов
/ 06 июля 2011

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

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

Он также может вызывать множество других видов связующего c # .

1 голос
/ 29 марта 2012

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

...