【问题标题】:Expression tree create dictionary with property values for class表达式树使用类的属性值创建字典
【发布时间】:2017-04-07 12:06:52
【问题描述】:

基本上我正在尝试使用表达式树来做到这一点

var properties = new Dictionary<string, object>();

foreach (var propInfo in objType.GetTypeInfo().GetProperties(BindingFlags.Public))
{
    var name = propInfo.Name;
    var value = propInfo.GetValue(objInstance);

    properties.Add(name, value);
}

return properties;

即创建名称和值对的字典,其中名称是objType 的属性名称,值是objType 的实例objInstance 的属性值

现在将其转换为表达式应该编译为简单的委托

Func<T, Dictionary<string, object>> func = i =>
{
    var properties = new Dictionary<string, object>();

    properties.Add("Prop1", (object)i.Prop1);
    properties.Add("Prop2", (object)i.Prop2);
    properties.Add("Prop3", (object)i.Prop3);
    // depending upon the number of properties of T, Add will continue

    return properties;
};

我知道如何执行其中的一些操作,但我不确定如何创建字典的本地实例,然后在后续表达式中使用它(并返回它)?

【问题讨论】:

  • 你可以使用 Expression.Constant(..) 从你的局部变量生成表达式
  • 表达式和委托是不同的东西。你到底想达到什么目的? Func&lt;object, Dictionary&lt;string, object&gt;&gt;Expression&lt;Func&lt;object, Dictionary&lt;string, object&gt;&gt;&gt;?
  • 抱歉,本地是指编译表达式的代表的本地,请参阅上面的代表主体修正。
  • 委托我的意思是编译表达式的结果

标签: c# reflection expression expression-trees


【解决方案1】:

应该类似于 (cmets inline):

public static Func<T, Dictionary<string, object>> GetValuesFunc<T>()
{
    Type objType = typeof(T);

    var dict = Expression.Variable(typeof(Dictionary<string, object>));
    var par = Expression.Parameter(typeof(T), "obj");

    var add = typeof(Dictionary<string, object>).GetMethod("Add", BindingFlags.Public | BindingFlags.Instance, null, new[] { typeof(string), typeof(object) }, null);

    var body = new List<Expression>();
    body.Add(Expression.Assign(dict, Expression.New(typeof(Dictionary<string, object>))));

    var properties = objType.GetTypeInfo().GetProperties(BindingFlags.Public | BindingFlags.Instance);

    for (int i = 0; i < properties.Length; i++)
    {
        // Skip write only or indexers
        if (!properties[i].CanRead || properties[i].GetIndexParameters().Length != 0)
        {
            continue;
        }

        var key = Expression.Constant(properties[i].Name);
        var value = Expression.Property(par, properties[i]);
        // Boxing must be done manually... For reference type it isn't a problem casting to object
        var valueAsObject = Expression.Convert(value, typeof(object));
        body.Add(Expression.Call(dict, add, key, valueAsObject));
    }

    // Return value
    body.Add(dict);

    var block = Expression.Block(new[] { dict }, body);

    var lambda = Expression.Lambda<Func<T, Dictionary<string, object>>>(block, par);
    return lambda.Compile();
}

像这样使用它:

public class Test
{
    public int A { get; set; }
    public string B { get; set; }
}

Func<Test, Dictionary<string, object>> fn = GetValuesFunc<Test>();

var obj = new Test
{
    A = 5,
    B = "Foo"
};

var res = fn(obj);

【讨论】:

  • 太棒了!非常感谢。
  • 在这里使用表达式有什么意义?这似乎过于复杂,不是吗?
  • @MBoros 那么你会提出什么解决方案?
【解决方案2】:

您只需要表达式来缓存 getter,除非您知道更简单的方法(例如,可能以某种方式动态?)

public static Func<T, Dictionary<string, object>> GetToDict<T>(){
    var getters = typeof(T)
        .GetProperties(BindingFlags.Public | BindingFlags.Instance)
        .Select(p => {
            var param = Expression.Parameter(typeof(T));
            return new KeyValuePair<PropertyInfo, Func<T, object>>(p, 
                Expression.Lambda<Func<T, object>>(Expression.Convert(Expression.MakeMemberAccess(param, p), typeof(object)), param).Compile());
        })
        .ToList();
    return i => getters.ToDictionary(x => x.Key.Name, x => x.Value(i));
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-28
    相关资源
    最近更新 更多