Domanda

Sto usando una gerarchia di classi di insiemi generici che derivano da una classe base astratta per memorizzare gli elementi di entità che derivano anche da una classe base astratta:

abstract class ItemBase { }

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


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

class MyCollection : CollectionBase<MyItem> { }

Gli obiettivi di questo modello sono di far rispettare tipo rigida disciplina sui membri di qualsiasi classe derivata da CollectionBase e per garantire che questi membri hanno un costruttore pubblico predefinito. Finora funziona.

Ora voglio creare un metodo factory che restituisce un'istanza di una classe derivata da CollectionBase . Capisco che il solito approccio potrebbe essere:

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

... o forse ...

public T Create CreateCollection<T>();

Tuttavia, il problema è che la routine chiamante non sa che cosa è necessaria "T". E 'il metodo di fabbrica stessa che deve determinare il tipo specifico di raccolta per tornare. Quindi ho bisogno di una firma metodo non generico che dice "il tipo di ritorno deriverà da CollectionBase ". Prevedo qualcosa di simile (se fosse legale) ...

public CollectionBase<> CreateCollection();

Presumo che ciò è un'altra di quelle spinose questioni varianza generici, ma anche dopo aver letto ampia spiegazione di Eric Lippert in materia, sono ancora chiaro se quello che sto cercando di fare è probabile che sia fattibile in C # 4.0, e se ci è una semplice soluzione in C # 3.0.

Grazie in anticipo per le vostre idee.

È stato utile?

Soluzione

Diciamo che il metodo factory sono stati la creazione di un singolo elemento. Avreste:

public ItemBase CreateItem();

Devi usare ItemBase perché non si può sapere qualcosa di più preciso. L'applicazione di tale principio per il metodo di raccolta, si ottiene:

public CollectionBase<ItemBase> CreateCollection();

A causa della mancanza della varianza, è necessario un adattatore di classe che deriva da CollectionBase<ItemBase> e avvolge la collezione reale che si crea.

In C # 4.0 si sarebbe solo restituire la collezione reale.

Modifica La fabbrica potrebbe assomigliare a questo, con l'adattatore incorporato:

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

Altri suggerimenti

Probabilmente quello che vi serve è:

public CollectionBase<Object> CreateCollection();

Dal momento che ogni classe eredita direttamente o indirettamente dalla classe Object.

Non ho effettivamente fatto questo, e le scuse se è completamente ingenuo, ma ...

public CollectionBase<Object> CreateCollection();

Se si vuole veramente che sia qualche cosa, si può solo tornare ICollection, e quindi i vostri consumatori potrebbe chiamare il metodo di estensione .Cast<ItemBase>() di cui hanno bisogno.

Da quello che ho capito, in NET 4.0 si sarebbe in grado di tornare CollectionBase<ItemBase>, ma non ho ancora provato sulla Beta.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top