如何在同一个集合中存储不同类型的通用类(都具有通用基类)?

时间:2012-08-08 15:39:37

标签: c# generics collections base-class

我遇到与this post中的问题类似的问题。

我还在网上看到成千上万的帖子,说你可以把任何扩展特定基类的泛型类放到基类类型的集合中。我完全理解这一点。

我的问题与上面的链接帖和其他基本方式不同 - 我的泛型类有一个通用的基类。更糟糕的是,这是一个非常大的MVVM应用程序框架(内部构建)的一部分,通用基类也有一个基类等等......每个基类都添加了某些属性和功能,它看起来有点像这样:

  

DataListEntry< T> < -BaseSynchronisableDataType< T> < -   BaseAuditDataType< - BaseDataType< - BaseAnimatableDataType

应用程序集合类使用类似的继承:

  

的DataList< T> < -BaseSynchronisableCollection< T> < -   BaseAnimatableCollection< T> < -BaseCollection< T> < -   SortableObservableCollection< T>

更糟糕的是,每个通用声明都有约束,例如,BaseSynchronisableDataType< T>的定义。看起来像这样:

public abstract class BaseSynchronisableDataType<T> : BaseAuditDataType, 
    ISynchronisable<T>, ICloneable<T>, IEquatable<T> where T : class, ICloneable<T>,
    IEquatable<T>, new()

因此,每个泛型集合类型都通过这些约束绑定到泛型基类。希望到现在为止,您可以看到我的问题的规模。

我尝试使用接口(上面没有显示)来删除从集合到各自基类的链接,但由于相关类的一些通用约束,这也失败了。例如,我无法创建接口类型的集合,因为在某些基类上存在必要的通用“类”约束,因此我得到错误,指出集合的类型'必须是非抽象类型,一个公共无参数构造函数,以便在泛型类型或方法'。

中将其用作参数'T'

最后一点需要注意的是我正在尝试做什么:

我需要使用不同的类来填充集合,这些类都扩展了DataList&lt; T&gt;。基类。这些类仅在名称上有所不同,并且在它们中具有完全相同的属性。它们声明如下:

public class Writer : DataListEntry<Writer>    
public class Artist : DataListEntry<Artist>etc.

如果您有任何想法,请告诉我...我已经在这个问题上遭受了2天的痛苦而我的老板也不高兴!非常感谢Sheridan。

3 个答案:

答案 0 :(得分:7)

这里有一个关键原则,您需要了解 - 即使Foo<Child>Foo<Parent>的子类,类Child也不是Parent 的子类1}} 的。这意味着List<Foo<Parent>>不能包含List<Foo<Child>>的实例,只能包含字符串或Int32。

要理解为什么会出现这种情况,想象下面的代码(不编译,但说明了为什么上述语句必须为真):

var myIntList = new List<Int>();
var myObjectList = (List<Object>)myIntList;

// Uh oh, now I can add a string to a list of integers...
myObjectList.Add("Foo");

您对curiously recurring template pattern的使用消除了所有类之间的继承层次结构。因为它们不再共享基类,所以不能将它们放入比List<Object>更具体的列表中

在您的情况下,最好的方法可能是创建一个DataListEntry实现的非泛型接口,并创建该接口的列表。如果界面提供了该类型实例中所需的所有成员,那么您已经完成了设置。

例如:

public interface IDataListEntry { 
    bool QuacksLikeADuck { get; } 
    bool WalksLikeADuck { get; }
}

public abstract class DataListEntry<T> : IDataListEntry where ... {
    // Implement these in subclasses
    abstract bool QuacksLikeADuck { get; } 
    abstract bool WalksLikeADuck { get; } 
}

然后你可以:

List<IDataListEntry> myDataListEntries = new List<IDataListEntry>();
myDataListEntries.Add(new Writer(...));
myDataListEntries.Add(new Artist(...));
IEnumerable ducks = myDataListEntries.Where(dle => dle.WalksLikeADuck && dle.QuacksLikeADuck);

或者(可能更适合您的情况),如果您需要知道T特定实例中IDataListEntry的类型:

public interface IDataListEntry { 
    Type TheTypeOfT { get; }
}

public class DataListEntry<T> : IDataListEntry where ... {
    Type TheTypeOfT { get { return typeof(T); } }
}

然后执行:

List<IDataListEntry> myDataListEntries = new List<IDataListEntry>();
myDataListEntries.Add(new Writer(...));
myDataListEntries.Add(new Artist(...));
IEnumerable artists = myDataListEntries.Where(dle => typeof(Artist).IsAssignableFrom(dle.TheTypeOfT));

答案 1 :(得分:0)

我会一直回到基础,并使用一些简单的linq来获取类筛选列表。

声明类型对象DataList<object> L的列表,然后当您被要求输入类型时,请调用L.OfType<Type>()来过滤列表。对象将是你可以使用的最通用的东西。你可能能够使用所有扩展的基类型,但因为它的摘要我不知道你是否可以声明该类型的列表。

在我自己的代码中,我使用泛型约束来实现类似的东西。

public abstract class BusinessObjectBase : //some interfaces
{
    //class stuff and events
}

我有一堆delcared的对象扩展了我的基类,现在我可以做到这一点

Collection<BusinessObjectBase> temp = new Collection<BusinessObjectBase>();
temp.Add(new RXEvents());
temp.Add(new RXBattery());
temp.Add(new RXBHA());

其中每个添加到列表中的类都是通过扩展BusinessObjectBase来创建的。您正在尝试非常相似的东西,但您的基本实现是不同的。通过将基本类型本身声明为模板化,您将打破基本类型。 Base与Note相同,除了object之外,两者不会实现任何共同点。

Base<>Base<X>Base<Y>无关。现在,如果你像这样贬低它

public abstract class BaseSynchronisableDataType<T> : BaseAuditDataType, ISynchronisable<T>, ICloneable<T>, IEquatable<T> where T : MyCustomBaseClass, ICloneable<T>, IEquatable<T>, new()

然后,您可以使用MyCustomBaseClass作为列表类型,因为您可以保证<T>代表的所有对象都是其子对象。这似乎打败了创建BaseSynchronisableDataType的目的......

答案 2 :(得分:0)

好的,问题是我无法声明以下内容

DataList<Artist> artists = new DataList<Artist>();

BaseDataListEntry类由于BaseSynchronisableCollection<T>扩展的通用DataList<T>类的通用约束而不是通用的时。它必须T属于BaseSynchronisableDataType<T>类无法扩展的类型BaseDataListEntry,因为它不是通用的。我需要BaseDataListEntry类不是通用的,以便我可以将DataList<T>的所有不同集合放入视图模型中的DataList<BaseDataListEntry>集合中(一次一个取决于用户的选择)。

解决方案是创建一个IBaseSynchronisableDataType<T>接口,并在通用约束中使用它而不是具体的BaseSynchronisableDataType<T>类。然后,我在BaseDataListEntry类中实现了它,所以现在满足所有约束。不知道为什么我之前没有看到它。

谢谢你所有的时间。