【问题标题】:Update values in a list with reflection使用反射更新列表中的值
【发布时间】:2012-05-16 09:20:40
【问题描述】:

我有一个包含 100 多个属性的对象。然后我有一个包含很多这些对象的列表。 我需要计算列表中所有属性的最小值、最大值、中值和中值。

所以不要写:

Ag = _valuesForExtraCalculations.Min(c => c.Ag),
Al = _valuesForExtraCalculations.Min(c => c.Al),
Alkal = _valuesForExtraCalculations.Min(c => c.Alkal),

那100次,我以为我可以使用反射,所以我写道:

var Obj = new Analysis();
Type t = Obj.GetType();
PropertyInfo[] prop = t.GetProperties();
foreach (var propertyInfo in prop)
{
    propertyInfo = _valuesForExtraCalculations.Min(?????);
}

但我不确定在 foreach 循环中要写什么,所以我可以将该属性的新最小值设置为我创建的新分析对象。

【问题讨论】:

  • 所有属性都属于同一类型吗?
  • 不,有些是字符串,但这不是主要问题,我总是可以检查该属性是否为 int,但是我应该如何编写来设置属性?
  • 那么,您想要一个新对象,在每个属性中都包含所有其他对象的该属性的平均值?
  • 是的,所以我有一个包含很多对象 A 的列表,其中包含很多属性,然后我想获取该部分属性的最小值,并将该值添加到新的 A 对象中,到同一个属性,在那里进行反射
  • 你还能改变对象的实现吗?

标签: c# .net list reflection


【解决方案1】:

据我了解您的问题,您可以使用表达式来实现:

/// <summary>
/// A class with many-many properties
/// </summary>
class MyClass
{
    public Decimal A { get; set; }
    public Decimal B { get; set; }
    public Decimal C { get; set; }
}

class PropertyHelper<T, TProperty>
{
    private readonly Func<T, TProperty> selector;
    private readonly Action<T, TProperty> setter;

    public PropertyHelper(Func<T, TProperty> selector, Action<T, TProperty> setter)
    {
        this.selector = selector;
        this.setter = setter;
    }

    public Func<T, TProperty> Selector
    {
        get { return selector; }
    }

    public Action<T, TProperty> Setter
    {
        get { return setter; }
    }
}

class AggregateHelper<T, TProperty>
{
    private readonly Dictionary<PropertyInfo, PropertyHelper<T, TProperty>> helpers;

    public AggregateHelper()
    {
        this.helpers = typeof(T)
            .GetProperties()
            .Where(p => p.PropertyType == typeof(TProperty))
            .ToDictionary(p => p, p => new PropertyHelper<T, TProperty>(MakeSelector(p), MakeSetter(p)));
    }

    private Func<T, TProperty> MakeSelector(PropertyInfo property)
    {
        var parameterExpr = Expression.Parameter(typeof(T));
        var lambda = (Expression<Func<T, TProperty>>)Expression.Lambda(
            Expression.Property(parameterExpr, property), parameterExpr);

        return lambda.Compile();
    }

    private Action<T, TProperty> MakeSetter(PropertyInfo property)
    {
        var instanceExpr = Expression.Parameter(typeof(T));
        var parameterValueExpr = Expression.Parameter(typeof(TProperty));
        var lambda = (Expression<Action<T, TProperty>>)Expression.Lambda(
            Expression.Call(instanceExpr, property.GetSetMethod(), parameterValueExpr),
            instanceExpr,
            parameterValueExpr);

        return lambda.Compile();
    }

    public IEnumerable<PropertyInfo> Properties
    {
        get { return helpers.Keys; }
    }

    public PropertyHelper<T, TProperty> this[PropertyInfo property]
    {
        get { return helpers[property]; }
    }
}

用法:

    public static void Do()
    {
        var target = new MyClass();
        var list = new List<MyClass>
        {
            new MyClass { A = 1M, B = 2M, C = 3M },
            new MyClass { A = 10M, B = 20M, C = 30M },
            new MyClass { A = 100M, B = 200M, C = 300M }
        };

        // calculate 'min' for all decimal properties
        var helper = new AggregateHelper<MyClass, Decimal>();

        foreach (var property in helper.Properties)
        {
            var propertyHelper = helper[property];

            propertyHelper.Setter(target, list.Min(propertyHelper.Selector));
        }
    }

编译后的 lambda 比反射工作得更快,并且不会有装箱/拆箱。

【讨论】:

    【解决方案2】:

    您需要知道属性的确切类型。假设它是int

    var Obj = new Analysis();
    Type t = Obj.GetType();
    PropertyInfo[] prop = t.GetProperties();
    foreach (var pi in prop.Where(p => p.PropertyType == typeof(int)) 
    {
        int min = _valuesForExtraCalculations.Min(c => (int)pi.GetValue(c, null));
        propertyInfo.SetValue(Obj, min, null);
    }
    

    如果您有不同的类型要计算最小值,您将无法检查类型并相应地切换,以便调用 Min 的正确重载。

    我仍然不会认为这是最好或最高效的解决方案。

    PropertyInfo.GetValuePropertyInfo.SetValue 比直接字段访问要慢(慢很多 according to this article),而且会涉及很多拳击。你会打电话给PropertyInfo.GetValue (countOfObjects * countOfProperties) 次。根据项目和属性的数量,这可能会引起关注。

    【讨论】:

    • 谢谢!最小值的提取工作正常,但设置正确的属性不起作用,这部分(propertyInfo.SetValue(this, min, null); 我得到 Object does not match target type
    • @Fore 我不小心插入了this 而不是你的变量。现在编辑我的答案。如果列表中的对象和Obj 属于同一类型,我更新的示例应该可以工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-02-24
    • 1970-01-01
    • 1970-01-01
    • 2016-03-15
    • 2012-05-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多