Как объявить метод, который возвращает общую коллекцию «что-нибудь» (C #) - PullRequest
2 голосов
/ 20 сентября 2009

Я использую иерархию общих классов коллекции, которые наследуются от абстрактного базового класса, для хранения элементов сущностей, которые также являются производными от абстрактного базового класса:

abstract class ItemBase { }

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


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

class MyCollection : CollectionBase<MyItem> { }

Задачи этой модели - обеспечить строгую дисциплину типов для членов любого класса, производного от CollectionBase , и гарантировать, что эти члены имеют открытый конструктор по умолчанию. Пока это работает.

Теперь я хочу создать фабричный метод, который возвращает экземпляр класса, производного от CollectionBase . Я понимаю, что обычный подход будет:

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

... или, может быть ...

public T Create CreateCollection<T>();

Однако проблема в том, что вызывающая подпрограмма не знает, какой "Т" требуется. Это сам фабричный метод, который должен определить конкретный тип возвращаемой коллекции. Поэтому мне нужна неуниверсальная сигнатура метода, которая говорит, что «возвращаемый тип будет производным от CollectionBase ». Я предполагаю что-то подобное (если бы это было законно) ...

public CollectionBase<> CreateCollection();

Я предполагаю, что это еще одна из этих непростых проблем общего характера, но даже после прочтения подробного объяснения Эрика Липперта по этому вопросу, я до сих пор не понимаю, возможно ли то, что я пытаюсь сделать, в C # 4.0 и возможно ли простой обходной путь в C # 3.0.

Заранее спасибо за ваши идеи.

Ответы [ 4 ]

1 голос
/ 20 сентября 2009

Допустим, фабричный метод создавал один элемент. Вы бы имели:

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
    }
}
0 голосов
/ 20 сентября 2009

Я на самом деле не сделал этого, и извиняюсь, если это полностью наивно, но ...

public CollectionBase<Object> CreateCollection();

0 голосов
/ 20 сентября 2009

Если вы действительно хотите, чтобы это было что-то, вы можете просто вернуть ICollection, и тогда ваши потребители могут вызывать метод расширения .Cast<ItemBase>() по мере необходимости.

Насколько я понимаю, в .NET 4.0 вы могли бы вернуть CollectionBase<ItemBase>, но я еще не пробовал его в бета-версии.

0 голосов
/ 20 сентября 2009

Вероятно, вам нужно:

public CollectionBase<Object> CreateCollection();

Поскольку каждый класс прямо или косвенно наследуется от класса Object.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...