Zuzlx Zuzlx - 3 months ago 8
C# Question

Sum up all the properties of a collection and dynamically assigned it to another object

I have a collection of object in

lst
of type
DataResponse
and what I would like to do is sum up all the properties that are
int
and
decimal
of this collection and assign the result of each property to another object
DataContainerResponse
that has the same exact property names(and types) as the those that are being summed up.

I can do this manually by typing out each property by hand and do a
.Sum(s=>s.<propertyname>
. But that so 90s. Below is my fruitless attempt to juice it out. Frankly, I never assigned a var to a lambda expression before and I don't even know if it's possible
.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]));

}
}

Answer

If you want to go with full reflection, you can try something like the following. I didnt optimize the code, did it as fast as I can. So sorry for the messy look and Im assuming the property names are same in the aggregated result class and the unit class that you are aggregating against.

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; }
}

But, as a suggestion, if you want to go with this approach, its better if you can cache all the reflection invokes you're making as they are costly. And the 90's approach would still win in benchmark. Like the example above would benchmark like the following with the simple StopWatch.

1.8193
0.4476
Press any key to continue . . .

The first one is the execution time of DoAggregationReflection and the last one is the execution time of DoAggregation. You can optimize the reflection one as much as you want but I think it would still fail to compete with the basic one.

Sometime's the 90's are way better. ;) Although you'd still use LINQ to do the actual summation so that's not that 90's anymore as LINQ was born in 2007 according to wikipedia.