如何声明一个返回“任何东西”的泛型集合的方法(C#)

时间:2009-09-20 10:22:03

标签: c# generics collections covariance contravariance

我使用的是泛型集合类的层次结构,它派生自一个抽象基类来存储也来自抽象基类的实体项:

abstract class ItemBase { }

class MyItem : ItemBase
{
    public MyItem()
    {
    }
}


abstract class CollectionBase<T> : Collection<T> where T : ItemBase, new() { }

class MyCollection : CollectionBase<MyItem> { }

该模型的目标是对源自CollectionBase&lt; T&gt;的任何类的成员强制执行严格的类型规则。并保证这些成员具有默认的公共构造函数。到目前为止它的确有效。

现在我想创建一个工厂方法,该方法返回从CollectionBase&lt; T&gt;派生的类的实例。我理解通常的方法是:

public CollectionBase<T> CreateCollection<T>();

...或者......或者......

public T Create CreateCollection<T>();

然而,问题是调用例程不知道需要什么“T”。工厂方法本身必须确定要返回的特定类型的集合。所以我需要一个非通用的方法签名,说“返回类型将派生自CollectionBase&lt; T&gt;”。我设想这样的事情(如果它是合法的)......

public CollectionBase<> CreateCollection();

我认为这是另一个棘手的通用差异问题,但即使在阅读了Eric Lippert关于这个主题的广泛解释之后,我仍然不清楚我想要做的是否可能在C#4.0中是可行的,以及是否存在是C#3.0中的一个简单的解决方法。

提前感谢您的想法。

4 个答案:

答案 0 :(得分:1)

假设工厂方法正在创建单个项目。你会:

public ItemBase CreateItem();

您必须使用ItemBase,因为您无法了解更具体的内容。将该原则应用于收集方法,您将得到:

public CollectionBase<ItemBase> CreateCollection();

由于缺乏差异,您需要一个从CollectionBase<ItemBase>派生的适配器类,并包装创建的实际集合。

在C#4.0中,您只需返回实际的集合。

编辑:工厂可能看起来像这样,嵌入了适配器:

public class CollectionFactory
{
    public CollectionBase<ItemBase> CreateCollection()
    {
        if(someCondition)
        {
            return CreateAdapter(new MyCollection());
        }
        else
        {
            return CreateAdapter(new MyOtherCollection());
        }
    }

    private static CollectionAdapter<T> CreateAdapter<T>(CollectionBase<T> collection) where T : ItemBase, new()
    {
        return new CollectionAdapter<T>(collection);
    }

    private class CollectionAdapter<T> : CollectionBase<ItemBase> where T : ItemBase, new()
    {
        private CollectionBase<T> _collection;

        internal CollectionAdapter(CollectionBase<T> collection)
        {
            _collection = collection;
        }

        // Implement CollectionBase API by passing through to _collection
    }
}

答案 1 :(得分:0)

您可能需要的是:

public CollectionBase<Object> CreateCollection();

由于每个类都直接或间接地从Object类继承。

答案 2 :(得分:0)

我实际上并没有这样做,如果完全天真,我会道歉,但......

public CollectionBase<Object> CreateCollection();

答案 3 :(得分:0)

如果你真的希望它成为任何东西,你可以只返回ICollection,然后你的消费者可以根据需要调用.Cast<ItemBase>()扩展方法。

根据我的理解,在.NET 4.0中你可以返回CollectionBase<ItemBase>,但我还没有尝试过它。