Обновление значения вложенного свойства через строковый путь с использованием выражения - PullRequest
0 голосов
/ 19 сентября 2018

Проблема, которую нужно решить

Я хочу иметь возможность обновить свойство prop, используя фиктивный метод Update.Для этого я хотел бы вызвать метод следующим образом:

Root obj = /* ... */;
Update(obj, "sub/sub/prop", "foobar");

Как бы я, например.построить какое-то дерево выражений для этого?

Сценарий

class Sub2
{
    public string prop { get; set; }
}
class Sub1
{
    public Sub2 sub { get; set; }
}
class Root
{
    public Sub1 sub { get; set; }
}
class Main
{
    //...
    void Update(object obj, string navigation, object newval) { /* magic */ }
}

Полная проблема

Мне нужно иметь возможность сериализовать отдельные поля из некоторого объекта (уже решено, глава метода)public void Serialize<TProperty>(T obj, Stream s, Expression<Func<T, TProperty>> exp)) и обновите соответствующее поле в приложении сервера.Только это поле может быть обновлено, некоторые классы вложены слишком глубоко, чтобы позволить такие решения, как «просто использовать некоторые идентификаторы и переключатель, чтобы затем поместить значение в правильное поле», именно поэтому этот подход был выбран.

Ответы [ 2 ]

0 голосов
/ 19 сентября 2018

Вы можете использовать рекурсию, чтобы перейти к свойству для обновления.Этот код ожидает, что все свойства вдоль пути не будут NULL, если вы можете иметь значения NULL, тогда будет легко иметь некоторый проверочный код для обработки этого случая (сгенерировать исключение и т. Д.).

void Update(object obj, string navigation, object newval)
{
    var firstSlash = navigation.IndexOf("/");
    if (firstSlash < 0)
    {
        obj.GetType().GetProperty(navigation).SetValue(obj, newval);
    }
    else
    {
        var header = navigation.Substring(0, firstSlash);
        var tail = navigation.Substring(firstSlash + 1);
        var subObj = obj.GetType().GetProperty(header).GetValue(obj);
        Update(subObj, tail, newval);
    }
}
0 голосов
/ 19 сентября 2018

Только что окончательно решил сам

public void Update1(T obj, string[] input, object newval)
{
    Type t = typeof(T);
    var param1 = Expression.Parameter(t);
    Expression exp = param1;
    foreach (var it in input.Skip(1).Take(input.Length - 2))
    {
        var minfo = t.GetProperty(it).GetGetMethod();
        exp = Expression.Call(exp, minfo);
        t = minfo.ReturnType;
    }
    var lastprop = t.GetProperty(input.Last());
    var minfoset = lastprop.GetSetMethod();
    var variableexp = Expression.Variable(lastprop.PropertyType);
    exp = Expression.Call(exp, minfoset, variableexp);
    var lambda = Expression.Lambda(exp, param1, variableexp);
    lambda.Compile().DynamicInvoke(obj, newval);
}
...