简体   繁体   English

我如何串联两个不同的列表 <T> 两者都有相同的基类?

[英]How can i concatenate two different list<T> where both have same base class?

I have a class called ItemChange<T> that looks like this: 我有一个叫做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;
}

As you can see it stores two copies of an object ( NewState and OldState ). 如您所见,它存储对象的两个副本( NewStateOldState )。 I use this to compare field changes. 我用它来比较字段更改。

I now am trying to get this to work where I get a list of changes across multiple objects and then concatenate a few different types of T list into one array like this (NOTE: both Object1 and Object2 derive from MyBase : 我现在正在尝试使其工作,在该操作中我获得多个对象的变化列表,然后将几种不同类型的T列表连接到一个数组中,如下所示(注意: Object1Object2都从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);
}

As you can see I need to concatenate changes from multiple types but then I want to grab the most recent number of changes (defined by numberOfChanges ) 如您所见,我需要将多种类型的更改串联起来,但是然后我想获取最新的更改数量(由numberOfChanges定义)

Any suggestions for how I could get the below to work as the Concat line gives me a compiler error (I assume I have to cast in some special way to get this to work). 关于如何使以下内容在Concat行中工作的任何建议都给了我一个编译器错误(我认为我必须以某种特殊的方式进行转换才能使其正常工作)。

Is there any way to do that? 有什么办法吗?

I recommend adding a base class to ItemChange<T> called ItemChange . 我建议向ItemChange<T>添加一个称为ItemChange的基类。 The ItemChange can declare the When property. ItemChange可以声明When属性。 Then it becomes pretty easy to cast the list contents to the base ItemChange for concat and order by. 然后,将列表内容ItemChange为基本的ItemChange以进行连接和排序变得非常容易。

using System;
using System.Collections.Generic;
using System.Linq;

namespace brosell
{

public class MyBase
{

}

public class ItemChange
{
    public DateTime When { get; set; }

}

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

public class Sub1: MyBase
{

}

public class Sub2: MyBase
{

}

   public class HelloWorld
   {
    public static void Main(string[] args)
    {
        List<ItemChange<Sub1>> listOfSub1 = new List<ItemChange<Sub1>>();
        List<ItemChange<Sub2>> listOfSub2 = new List<ItemChange<Sub2>>();

        var concated = listOfSub1.Cast<ItemChange>().Concat(listOfSub2.Cast<ItemChange>());

        var filtered = concated.OrderByDescending(ic => ic.When).Take(10);  

        Console.WriteLine("{0}", filtered.Count());
        }
   } 
}

How about this? 这个怎么样? I am not sure because I cannot test it now. 我不确定,因为我现在无法测试。

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

It creates a new instance of ItemChange<MyBase> from ItemChange<Object1> or ItemChange<Object2> . 它从ItemChange<Object1>ItemChange<Object2>创建ItemChange<MyBase>的新实例。

Depending on your usage, you may want to add .ToList() to the end of the Linq to increase performance. 根据您的使用情况,您可能需要在Linq的末尾添加.ToList()以提高性能。

What you are trying to do is not safe since there is no conversion between an ItemChange<Object1> and an ItemChange<Object2> , and there is certainly no conversion to some arbitrary ItemChange<T> . 您尝试执行的操作并不安全,因为在ItemChange<Object1>ItemChange<Object2>之间没有转换,并且当然也没有到任意的ItemChange<T>转换。 The best you can try to do is ItemChange<MyBase> but classes are not covariant in C# so this is not valid: 您可以尝试做的最好的事情是ItemChange<MyBase>但是C#中的类不是协变的,因此这是无效的:

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

You therefore cannot cast an IEnumerable<ItemChange<Object1>> to an IEnumerable<ItemChange<Object2>> . 因此,您不能将IEnumerable<ItemChange<Object1>>转换为IEnumerable<ItemChange<Object2>>

However if you create an interface for your ItemChange<T> class then you can do it safely: 但是,如果为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; }
}

You can then change your GetChanges and GetChangeHistory methods to: 然后,您可以将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);
}

If you defined an interface IReadableItemChange<out T> which exposed read-only properties of type T rather than fields of that type, and if ItemChange<T> implemented IReadableItemChange<T> , then both ItemChange<Derived1> and ItemChange<Derived2> would implement IReadableItemChange<MyBase> (and, incidentally, IReadableItemChange<baseType> for any baseType such that Derived1:baseType and Derived2:baseType ). 如果定义了接口IReadableItemChange<out T> ,该接口公开了类型T只读属性,而不是该类型的字段,并且如果ItemChange<T>实现了IReadableItemChange<T> ,则ItemChange<Derived1>ItemChange<Derived2>都将实现IReadableItemChange<MyBase> (以及对任何baseType例如Derived1:baseTypeDerived2:baseType )的IReadableItemChange<baseType> )。 It may be helpful as well for ItemChange<T> to have a constructor which accepts an IReadableItemChange<T> and copies the data from it. 对于ItemChange<T> ,拥有一个接受IReadableItemChange<T>并从中复制数据的构造函数也可能会有所帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 我如何合并两个对象的类和通用列表 <T> 使用LINQ - How can i merge two object both Class and Generic List<T> use LINQ 如何定义一个接口方法,其不同的实现可以返回<t> , 列表<t> , 列表<list<t> &gt; 其中 T 是自定义 class </list<t></t></t> - How can I define an interface method whose different implementation can return <T>, List<T>, List<List<T>> where T is a custom class 我可以拥有/使用带有列表的字段/属性吗 <Class<T> &gt; T可以是什么? - Can I have/use a field/property with a List<Class<T>> where T can be anything? 如果我有两个接口,那么一个类可以继承两个接口吗? - If I have two interfaces, can one class inherit both? 使用 Fluent Validation 如何检查对象中的两个属性都不能有值? - Using Fluent Validation how can I check that two properties in an object both can't have a value? 将具有相同基类的两个不同类放入同一个列表的更好方法? - Better Way To Put Two Different Classes w/ Same Base Class In Same List? 我怎么知道 class 都来自同一个通用 class - How can I know both class are from same Generic class 如何连接列表 - How can I concatenate a list 如何在列表上使用LINQ <T> 哪里T没有任何嵌套元素? - How can I use LINQ on a List<T> where T doesn't have any nested elements? 将列表复制到另一个列表中,其中都有其他类 - Copy a list to a different list where both have other classes in
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM