在多态情况下使用Linq聚合重复记录

时间:2017-06-15 09:16:48

标签: c# linq

我想汇总存储在List<>中的记录。

如果我得到List<int>,解决方案就是

var results = list.GroupBy(x => x).Select(g => g.Sum());

如果我得到List<MyObject>

public class MyObject
{
   public MyObject(int pvalue)
   {
      Value = pvalue;
   }

   public int Value {get; set;}

   public override bool Equals(object obj)
   {
       var p = obj as MyObject;
       if (p == null)
          return false;

       return Value.Equals(p.Value);
   }

   public bool Equals(MyObject p)
   {
     if (p == null)
        return false;

     return Value.Equals(p.Value)
   }

   public override int GetHashCode()
   {
     int hash = 13;
     hash = (hash * 7) + Value.GetHashCode();
     return hash;
   }
 }

然后解决方案是: myOjectList.GroupBy(x => x.Value).Select(g => new MyObject{ Value = g.Sum()});

现在,在我得到List<IMyObject>IMyObject作为接口(或抽象类)的情况下,其具体实现得到了特定的属性(下面的示例类),我该如何解决多态性在之前的Linq声明的Select中?

public interface IMyObject
{
   int Value {get; set;}
   bool Equals(object obj);
   int GetHashCode();
}

public class MyObject1 : IMyObject
{
   public MyObject1(int pvalue, string pname)
   {
      Value = pvalue;
      Name = pname;
   }

   public int Value {get; set;}
   public string Name { get; set; }

   public override bool Equals(object obj)
   {
       var p = obj as MyObject1;
       if (p == null)
          return false;

       return Name.Equals(p.Name) && Value.Equals(p.Value);
  }

  public bool Equals(MyObject1 p)
  {
     if (p == null)
        return false;

     return Name.Equals(p.Name) && Value.Equals(p.Value)
  }

  public override int GetHashCode()
  {
     int hash = 13;
     hash += (hash * 7) + Value.GetHashCode();
     hash += (hash * 7) + Name.GetHashCode();
     return hash;
  }
}

public class MyObject2 : IMyObject
{
   public MyObject2(int pvalue, int pvalue2)
   {
      Value = pvalue;
      Value2 = pvalue2;
   }

   public int Value {get; set;}
   public string Value2 { get; set; }

   public override bool Equals(object obj)
   {
       var p = obj as MyObject1;
       if (p == null)
          return false;

       return Value2.Equals(p.Value2) && Value.Equals(p.Value);
  }

  public bool Equals(MyObject1 p)
  {
     if (p == null)
        return false;

     return Value2.Equals(p.Value2) && Value.Equals(p.Value)
  }

  public override int GetHashCode()
  {
     int hash = 13;
     hash += (hash * 7) + Value.GetHashCode();
     hash += (hash * 7) + Value2.GetHashCode();
     return hash;
  }
}

2 个答案:

答案 0 :(得分:1)

FIRST SOLUTION: REFLECTION

myListOfIMyObjects
    .GroupBy(x => x.GetType())
    .Select(x =>
        {
            var constr = x.Key.GetConstructor(Type.EmptyTypes);
            var instance = (IMyObject)constr.Invoke(new object[0]);
            instance.Value = x.Select(o => o.Value).Sum();
            return instance;
        })
    .ToList();

Pro: you can embed all in a single Select, valid for all types implementing IMyObject, and you don't have to modify it if you add other classes like MyObject3 or MyObject4 later.

Con: Reflection is a fragile pattern, because you cannot rely on compilation checks. Also, all the MyObject classes must expose a parameterless constructor, and if it is not so, you will see an error only at runtime.


SECOND SOLUTION: OfType

var result1 = myListOfIMyObjects.OfType<MyObject1>();
var o1 = new MyObject1 { Value = result1.Sum(x => x.Value) };

var result2 = myListOfIMyObjects.OfType<MyObject2>();
var o2 = new MyObject2 { Value = result2.Sum(x => x.Value) };

var result = new List<IMyObject> { o1, o2 };

Pro: you have the compile-time checks, and the classes implementing IMyObject can have different constructors with different parameters.

Con: It's more verbose (you cannot embed that code in a single Select!), and if you add later other MyObject3, MyObject4 in your domain, you have to come back here and add other rows manually.

答案 1 :(得分:1)

IMyObject添加新操作到Clone对象:

public interface IMyObject {
    int Value { get; set; }
    bool Equals(object obj);
    int GetHashCode();
    IMyObject Clone();
}

实施Clone方法:

public class MyObject1 : IMyObject {
    ...
    public IMyObject Clone() {
        return (IMyObject)this.MemberwiseClone();
    }
}

public class MyObject2 : IMyObject {
    ...
    public IMyObject Clone() {
        return (IMyObject)this.MemberwiseClone();
    }
}

现在您可以使用Clone方法创建要返回的对象(注意:您没有说明如何为其他属性选择正确的值,因此我随意使用每个组中的第一个对象作为源)。

var ans = myObjectList.GroupBy(x => x.Value).Select(g => { var rtnval = g.First().Clone(); rtnval.Value = g.Sum(m => m.Value); return rtnval; });