汇总集合的所有属性并动态地将其分配给另一个对象



我在lst中有一个类型为DataResponse的对象集合,我想做的是总结该集合中intdecimal的所有属性,并将每个属性的结果分配给另一个对象DataContainerResponse,该对象具有与正在总结的那些完全相同的属性名称(和类型)。

我可以通过手动输入每个属性并执行.Sum(s=>s.<propertyname>来手动执行此操作。但那是90年代。下面是我徒劳的尝试。坦率地说,我以前从来没有给lambda表达式分配过var,我甚至不知道这是否可能.Sum(s=><var name>);

 public DataAggragationResponse doAggregation(List<DataResponse> lst)
    {
        if (lst.Count == 0)
            return null;
        DataContainerResponse rd = new DataContainerResponse();
        //If I do it manually typing each prop by hand.
        rd.VIOL = lst.Sum(s => s.VIOL);

        //Automation!!!
        foreach (PropertyInfo propertyInfo in typeof(DataResponse).GetProperties())
        {
                rd.GetType().GetProperties().SetValue(lst.Sum(s => propertyInfo.Name[0]));
        }
    }

如果您想使用完全反射,您可以尝试以下操作。我没有优化代码,只是尽可能快地完成它。抱歉看起来很乱,我假设属性名在聚合结果类和你要聚合的单位类中是相同的。

class Program
{
    static void Main(string[] args)
    {
        var list = new List<DataResponse>();
        list.Add(new DataResponse() { Stuff = 1, Stuff2 = 2 });
        list.Add(new DataResponse() { Stuff = 1, Stuff2 = 2 });
        Stopwatch watch = new Stopwatch();
        watch.Start();
        var response = DoAggregationReflection(list);
        watch.Stop();
        Console.WriteLine(watch.Elapsed.TotalMilliseconds);
        watch.Reset();
        watch.Start();
        var response2 = DoAggregation(list);
        watch.Stop();
        Console.WriteLine(watch.Elapsed.TotalMilliseconds);
    }
    public static DataAggragationResponse DoAggregationReflection(List<DataResponse> lst)
    {
        if (lst.Count == 0)
            return null;
        DataAggragationResponse aggrResponse = new DataAggragationResponse();
        var responseType = typeof(DataResponse);
        var aggrResponseType = typeof(DataAggragationResponse);
        foreach (PropertyInfo propertyInfo in typeof(DataResponse).GetProperties())
        {
            aggrResponseType.GetProperty(propertyInfo.Name).SetValue(aggrResponse, lst.Sum(x => (int)responseType.GetProperty(propertyInfo.Name).GetValue(x)));
        }
        return aggrResponse;
    }
    public static DataAggragationResponse DoAggregation(List<DataResponse> lst)
    {
        if (lst.Count == 0)
            return null;
        DataAggragationResponse aggrResponse = new DataAggragationResponse();
        aggrResponse.Stuff = lst.Sum(x => x.Stuff);
        aggrResponse.Stuff2 = lst.Sum(x => x.Stuff2);
        return aggrResponse;
    }
}

public class DataResponse
{
    public int Stuff { get; set; }
    public int Stuff2 { get; set; }
}
public class DataAggragationResponse
{
    public int Stuff { get; set; }
    public int Stuff2 { get; set; }
}

但是,作为一个建议,如果你想采用这种方法,最好能缓存你所做的所有反射调用,因为它们是昂贵的。90年代的方法仍然会在基准上胜出。就像上面的例子可以像下面这样对简单的StopWatch进行基准测试。

1.8193
0.4476
Press any key to continue . . .

第一个是DoAggregationReflection的执行时间,最后一个是DoAggregation的执行时间。你可以尽可能地优化反射,但我认为它仍然无法与基本的那个竞争。

有时候90年代的要好得多。;) 虽然你仍然会用LINQ来做实际的求和,所以这不是90年代了,因为LINQ是在2007年根据维基百科诞生的。

希望这能帮到你。我希望我保留了这个问题的SO链接,我从一段时间前拉了这个。很抱歉没有提到他/她的名字。

using System.Reflection;
public static Dictionary<string, string> GetPropertiesValue(object o)
    {
        Dictionary<string, string> PropertiesDictionaryToReturn = new Dictionary<string, string>();
        foreach (MemberInfo itemMemberInfo in o.GetType().GetMembers())
        {
            if (itemMemberInfo.MemberType == MemberTypes.Property)
            {
                //object PropValue = GetPropertyValue(OPSOP, item.Name);
                //string itemProperty = itemMemberInfo.Name;
                //string itemPropertyValue = o.GetType().GetProperty(itemMemberInfo.Name).GetValue(o, null).ToString();
                //Console.WriteLine(itemProperty + " : " + itemPropertyValue);
                PropertiesDictionaryToReturn.Add(itemMemberInfo.Name, o.GetType().GetProperty(itemMemberInfo.Name).GetValue(o, null).ToString());
            }
        }
        return PropertiesDictionaryToReturn;
    }

这不是你所需要的,但是,我认为你可以改变它。

我宁愿采取不同的方法。我会动态构建和编译(一次)这样的东西:

Func<DataContainerResponse, DataResponse, DataContainerResponse> aggregateFunc =
(result, item) =>
{
    result.Prop1 += item.Prop1;
    result.Prop2 += item.Prop2;
    ...
    result.PropN += item.PropN;
    return result;
}

(如果你想知道为什么签名像上面那样,答案是——因为它可以直接用于下面的Aggregate过载)

可以这样做:

static readonly Func<DataContainerResponse, DataResponse, DataContainerResponse> 
    AggregateFunc = BuildAggregateFunc();
static Func<DataContainerResponse, DataResponse, DataContainerResponse> BuildAggregateFunc()
{
    var result = Expression.Parameter(typeof(DataContainerResponse), "result");
    var item = Expression.Parameter(typeof(DataResponse), "item");
    var propertyTypes = new HashSet<Type> { typeof(decimal), typeof(int) };
    var statements = item.Type.GetProperties()
            .Where(p => propertyTypes.Contains(p.PropertyType))
            .Select(p => Expression.AddAssign(
                Expression.Property(result, p.Name),
                Expression.Property(item, p)));
    var body = Expression.Block(statements
        .Concat(new Expression[] { result }));
    var lambda = Expression.Lambda<Func<DataContainerResponse, DataResponse, DataContainerResponse>>(
        body, result, item);
    return lambda.Compile();
}

,用法很简单:

public DataContainerResponse DoAggregation(List<DataResponse> source)
{
    return source.Aggregate(new DataContainerResponse(), AggregateFunc);
}

相关内容

  • 没有找到相关文章

最新更新