Pergunta

Eu estou usando uma hierarquia de classes de coleção genérica que derivam de uma classe base abstrata para itens entidade loja que também derivam de uma classe base abstrata:

abstract class ItemBase { }

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


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

class MyCollection : CollectionBase<MyItem> { }

Os objetivos deste modelo são para impor tipo estrita disciplina sobre os membros de qualquer classe derivada de CollectionBase e garantia de que estes membros têm um construtor público padrão. Até agora ele funciona.

Agora eu quero criar um método de fábrica que retorna uma instância de uma classe derivada de CollectionBase . Eu entendo que a abordagem usual seria:

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

... ou talvez ...

public T Create CreateCollection<T>();

No entanto, o problema é que a rotina de chamada não sabe o que "T" é necessária. É o método de fábrica em si que deve determinar o tipo específico de coleta de retorno. Então eu preciso de uma assinatura de método não-genérico que diz "o tipo de retorno irá derivar de CollectionBase ". I imaginar algo assim (se fosse legal) ...

public CollectionBase<> CreateCollection();

Eu suponho que este é mais um daqueles problemas de variância genéricos espinhosos, mas mesmo depois de ler extensa explicação de Eric Lippert sobre o assunto, eu sou ainda claro se o que eu estou tentando fazer é provável que seja viável em C # 4.0, e se há é uma simples solução em C # 3.0.

Agradecemos antecipadamente por suas idéias.

Foi útil?

Solução

Vamos dizer que o método de fábrica estavam criando um único item. Você teria que:

public ItemBase CreateItem();

Você tem que usar ItemBase porque você não pode saber algo mais específico. Aplicando este princípio ao método de coleta, você obtém:

public CollectionBase<ItemBase> CreateCollection();

Devido à falta de variância, você precisa de um adaptador classe que deriva de CollectionBase<ItemBase> e envolve a coleção real que é criado.

Em C # 4.0 você teria apenas que retornar a coleção real.

Editar: A fábrica pode ter esta aparência, com o adaptador incorporado:

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

Outras dicas

Provavelmente o que você precisa é:

public CollectionBase<Object> CreateCollection();

Uma vez que cada classe direta ou indiretamente herda da classe de objeto.

Eu realmente não tenho feito isso, e desculpas se do completamente ingênuo, mas ...

public CollectionBase<Object> CreateCollection();

?

Se você realmente quer que seja qualquer coisa, você poderia simplesmente voltar ICollection, e depois seus consumidores poderia chamar o método de extensão .Cast<ItemBase>() como eles precisam.

Pelo que eu entendo, em .NET 4.0 você seria capaz de retornar CollectionBase<ItemBase>, mas eu não tentei isso na Beta ainda.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top