1

I have collection of custom objects(assets) which I want to group with LINQ. Custom object has standard properties like id, name and cost property. When grouping I want to calculate cost for each group, so I'm using little trick like this:

from a in assets
group a by a.AssetId into ga
select new Asset()
                 {
                    AssetId = ga.Key,
                    Cost = ga.Select(gg=>gg.Cost).Sum()
                 }

Ok, everything is fine here. But...in order to initialize order properties as well, I'm using copy contructor and cost calculations together...

from a in assets
group a by a.AssetId into ga
select new Asset(ga.FirstOrDefault())
                 {
                    AssetId = ga.Key,
                    Cost = ga.Select(gg=>gg.Cost).Sum()
                 }

So now, I get collection of grouped assets by id, with all properties copied from a first asset in a group with calculated grouped cost. But...in order to do that I need to write for every object that using this kind of grouping, a copy constructor with 'all properties initialization' which in my case is overhead because there are objects with 20+ properties.

I've tried to use clone trick from a link:

Deep cloning objects

in linq group query but with no success.

My question: is there a better/more elegant way to accomplish this?

Thank you

Community
  • 1
  • 1
Marko
  • 1,874
  • 1
  • 21
  • 36

1 Answers1

2

If you don't need deep copy them you can use MemberwiseClone method. The implementation (comes from Object class) & gives you a shallow copy. So line new Asset(ga.FirstOrDefault()) will become ((Asset)(ga.FirstOrDefault().MemberwiseClone())). However, being shallow copy, [First Asset].SomeObject would point to [Grouped Asset].SomeObject and changes from one would reflect at other.

Edited to include helper method described in comments:

static T CloneAndUpdate<T>(T t, Action<T> updater) where T: class
{
T clone = null; // Use reflection/serialization to create shallow/deep clone
updater(clone);
return clone;
}

Now, you may use the method as follows:

select Utility.CloneAndUpdate(ga.FirstOrDefault(), a =>
             {
                AssetId = ga.Key,
                Cost = ga.Select(gg=>gg.Cost).Sum()
             })
VinayC
  • 47,395
  • 5
  • 59
  • 72
  • Oops! I notice that you want to use object initializers - not sure if they would work on Asset instance w/o using constructor. – VinayC Aug 11 '10 at 10:12
  • Only way that I can think of is to add copy constructor that uses MemberwiseClone() - that will reduce at least time to write copy constructor. Another way would be to write a static method such as -- static T CloneAndUpdate(T t, Action updater) where T: class { T clone = ...; /* Use reflection/serialization to create clone */ updater(clone); return clone; } then use it as ---- select Utility.CloneAndUpdate(ga.FirstOrDefault(), a => { AssetId = ga.Key, Cost = ga.Select(gg=>gg.Cost).Sum() }) – VinayC Aug 11 '10 at 10:35
  • hmm, last comment is a nice solution, could you post it as a answer please, it is little unreadable like this...thanx :)..oh, you did it already...ok thats it, thank you very much, I like your solution...+1 ;) and marked as answer :) – Marko Aug 11 '10 at 12:04