我如何连接两个具有相同基类的不同列表<t>?</t>

时间:2012-11-11 14:01:03

标签: c# generics collections

我有一个名为ItemChange<T>的课程,如下所示:

public class ItemChange<T> where T : MyBase
{
    public DateTime When { get; set; }
    public string Who { get; set; }
    public T NewState;
    public T OldState;
}

如您所见,它存储了一个对象的副本(NewStateOldState)。我用它来比较字段变化。

我现在正试图让这个工作,我得到一个跨多个对象的更改列表,然后将几个不同类型的T列表连接成一个这样的数组(注意:Object1和{{1从Object2派生:

MyBase

正如您所看到的,我需要连接多种类型的更改,但之后我想获取最新数量的更改(由public IEnumerable<ItemChange<T>> GetChangeHistory<T>(int numberOfChanges) where T : MyBase { IEnumerable<ItemChange<Object1>> obj1Changes= GetChanges<Object1>(); IEnumerable<ItemChange<Object2>> obj1Changes= GetChanges<Object2>(); return obj1Changes.Concat(obj2Changes).OrderByDescending(r => r.When).Take(numberofChanges); } 定义)

关于如何让以下工作作为Concat行的任何建议都会给我一个编译器错误(我假设我必须采用一些特殊的方式来使其工作)。

有没有办法做到这一点?

4 个答案:

答案 0 :(得分:4)

我建议将基类添加到名为ItemChange<T>的{​​{1}}。 ItemChange可以声明When属性。然后,将列表内容强制转换为基础ItemChange以进行连接并按顺序排序。

ItemChange

答案 1 :(得分:1)

这个怎么样?我不确定,因为我现在无法测试它。

public IEnumerable<ItemChange<T>> GetChangeHistory<T>(int numberOfChanges) where T : MyBase
{
    IEnumerable<ItemChange<MyBase>> obj1Changes = GetChanges<Object1>().Select(i => new ItemChange<MyBase>(){ When = i.When, Who = i.Who, NewState = i.NewState, OldState = i.OldState });
    IEnumerable<ItemChange<MyBase>> obj1Changes = GetChanges<Object2>().Select(i => new ItemChange<MyBase>(){ When = i.When, Who = i.Who, NewState = i.NewState, OldState = i.OldState });
    return obj1Changes.Concat(obj2Changes).OrderByDescending(r => r.When).Take(numberofChanges);
}

它会从ItemChange<MyBase>ItemChange<Object1>创建ItemChange<Object2>的新实例。

根据您的使用情况,您可能希望将.ToList()添加到Linq的末尾以提高性能。

答案 2 :(得分:1)

您尝试做的事情并不安全,因为ItemChange<Object1>ItemChange<Object2>之间没有转换,并且肯定没有转换为某些任意ItemChange<T>。您可以尝试做的最好的是ItemChange<MyBase>但是C#中的类不是协变的,所以这是无效的:

ItemChange<MyBase> change = new ItemChange<Object1>();

因此,您无法将IEnumerable<ItemChange<Object1>>投射到IEnumerable<ItemChange<Object2>>

但是,如果您为ItemChange<T>课程创建界面,则可以安全地进行:

public interface IItemChange<out T> where T : MyBase
{
    DateTime When { get; set; }
    string Who { get; set; }
    T NewState { get; }
    T OldState { get; }
}

public class ItemChange<T> : IItemChange<T> where T : MyBase
{
    public DateTime When { get; set; }
    public string Who { get; set; }
    public T NewState { get; set; }
    public T OldState { get; set; }
}

然后,您可以将GetChangesGetChangeHistory方法更改为:

private static IEnumerable<IItemChange<T>> GetChanges<T>() where T : MyBase { ... }

public static IEnumerable<IItemChange<MyBase>> GetChangeHistory(int numberOfChanges)
{
    IEnumerable<IItemChange<MyBase>> obj1Changes = GetChanges<Object1>();
    IEnumerable<IItemChange<MyBase>> obj2Changes = GetChanges<Object2>();
    return obj1Changes.Concat(obj2Changes).OrderByDescending(r => r.When).Take(numberOfChanges);
}

答案 3 :(得分:0)

如果您定义了一个接口IReadableItemChange<out T>,它显示了T类型的只读属性,而不是该类型的字段,并且ItemChange<T>已实现IReadableItemChange<T>,则{ {1}}和ItemChange<Derived1>会为ItemChange<Derived2>实现IReadableItemChange<MyBase>IReadableItemChange<baseType> baseTypeDerived1:baseTypeDerived2:baseType。对于ItemChange<T>来说,拥有一个接受IReadableItemChange<T>并从中复制数据的构造函数也可能会有所帮助。