Как установить свойство динамического объекта С# 4, если у вас есть имя в другой переменной

Я ищу способ изменения свойств объекта dynamic С# 4.0 с именем свойства, известного только во время выполнения.

Есть ли способ сделать что-то вроде (ExpandoObject просто используется в качестве примера, это может быть любой класс, который реализует IDynamicMetaObjectProvider):

string key = "TestKey";
dynamic e = new ExpandoObject();
e[key] = "value";

Что было бы эквивалентно:

dynamic e = new ExpandoObject();
e.TestKey = "value";

Или это единственный способ отражения?

Ответ 1

Не очень легко, нет. Отражение не работает, так как оно предполагает модель обычного типа, которая не является полным диапазоном dynamic. Если вы на самом деле просто разговариваете с обычными объектами, тогда просто используйте отражение здесь. В противном случае я ожидаю, что вы захотите перепроектировать код, который компилятор испускает для базового присвоения, и настроить его на гибкое имя участника. Однако я буду честен: это не привлекательный вариант; просто:

dynamic foo = ...
foo.Bar = "abc";

переводит на:

if (<Main>o__SiteContainer0.<>p__Site1 == null)
{
    <Main>o__SiteContainer0.<>p__Site1 = CallSite<Func<CallSite, object, string, object>>.Create(Binder.SetMember(CSharpBinderFlags.None, "Bar", typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant | CSharpArgumentInfoFlags.UseCompileTimeType, null) }));
}
<Main>o__SiteContainer0.<>p__Site1.Target(<Main>o__SiteContainer0.<>p__Site1, foo, "abc");

Если вам нужен подход, который работает как для динамических, так и для нединамических объектов: FastMember удобен для этого и работает либо с типом или уровня объекта:

// could be static or DLR 
var wrapped = ObjectAccessor.Create(obj); 
string propName = // something known only at runtime 
Console.WriteLine(wrapped[propName]);

доступен в Nuget и сильно оптимизирован для динамических и нединамических сценариев.

Ответ 2

Пол Сасик ответил на аналогичный вопрос в С# 4.0 Dynamic vs Expando... где они подходят?

using System;
using System.Dynamic;

class Program
{
    static void Main(string[] args)
    {
        dynamic expando = new ExpandoObject();
        var p = expando as IDictionary<String, object>;
        p["A"] = "New val 1";
        p["B"] = "New val 2";

        Console.WriteLine(expando.A);
        Console.WriteLine(expando.B);
    }
}

Ответ 3

Чтобы добавить к ответу Джонаса, вам не нужно создавать новый var p. Вместо этого используйте этот подход:

using System;
using System.Dynamic;

class Program
{
    static void Main(string[] args)
    {
        dynamic expando = new ExpandoObject();
        ((IDictionary<String, object>)expando)["A"] = "New val 1";
        ((IDictionary<String, object>)expando)["B"] = "New val 2";

        Console.WriteLine(expando.A);
        Console.WriteLine(expando.B);
    }
}

Ответ 4

В моей открытой исходной структуре Dynamitey есть методы для вызова на основе имен строк с использованием DLR. Он выполняет работу кэширования сайтов привязки и упрощает его до одного вызова метода. он также работает быстрее, чем отражение на нединамических объектах.

Dynamic.InvokeSet(e, "TestKey", "value");

Ответ 5

fast-member может соответствовать счету - похоже, что он генерирует IL на лету, но кэширует его так быстро, как только первое использование.