Question

J'utilise une hiérarchie de classes de collections génériques qui dérivent d'une classe de base abstraite pour stocker des éléments d'entités qui dérivent également d'une classe de base abstraite:

abstract class ItemBase { }

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


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

class MyCollection : CollectionBase<MyItem> { }

Les objectifs de ce modèle sont de faire respecter la discipline stricte de type sur les membres d'une classe dérivée de CollectionBase et de garantir que ces membres ont un défaut constructeur public. Jusqu'à présent, cela fonctionne.

Maintenant, je veux créer une méthode de fabrication qui renvoie une instance d'une classe dérivée de CollectionBase . Je comprends que l'approche habituelle serait:

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

... ou peut-être ...

public T Create CreateCollection<T>();

Cependant, le problème est que la routine d'appel ne sait pas ce que « T » est nécessaire. Il est la méthode de l'usine elle-même qui doit déterminer le type de collection pour revenir. Alors je besoin d'une signature de la méthode non-générique qui indique que « le type de retour dérivera de CollectionBase ». Je prévois quelque chose comme ça (si elle était légale) ...

public CollectionBase<> CreateCollection();

Je suppose que ceci est une autre de ces questions de variance génériques épineux, mais même après avoir lu l'explication approfondie de Eric Lippert sur le sujet, je ne suis toujours pas clair si ce que je suis en train de faire est susceptible d'être réalisable dans C # 4.0, et si est une solution simple en C # 3.0.

Merci d'avance pour vos idées.

Était-ce utile?

La solution

Disons que la méthode de l'usine ont été la création d'un seul élément. Vous auriez:

public ItemBase CreateItem();

Vous devez utiliser ItemBase parce que vous ne pouvez pas savoir quoi que ce soit plus spécifique. L'application de ce principe à la méthode de collecte, vous obtenez:

public CollectionBase<ItemBase> CreateCollection();

En raison du manque de variance, vous avez besoin d'une classe d'adaptateur qui dérive de CollectionBase<ItemBase> et enveloppe la collection réelle qui est créé.

En C # 4.0 vous suffit de retourner la collection réelle.

Modifier L'usine pourrait ressembler à ceci, avec l'adaptateur intégré:

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

Autres conseils

Probablement ce que vous avez besoin est:

public CollectionBase<Object> CreateCollection();

Étant donné que chaque classe hérite directement ou indirectement de la classe Object.

Je ne l'ai pas fait en fait cela, et des excuses si elle est complètement naïve, mais ...

public CollectionBase<Object> CreateCollection();

Si vous voulez vraiment être quoi que ce soit, vous pouvez simplement revenir ICollection, puis vos consommateurs pourrait appeler la méthode d'extension de .Cast<ItemBase>() comme ils ont besoin.

D'après ce que je comprends, .NET 4.0, vous seriez en mesure de revenir CollectionBase<ItemBase>, mais je ne l'ai pas essayé sur la version bêta encore.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top