2009-09-08 14 views
2

J'essaye d'écrire une méthode d'usine qui créera une instance dérivée d'une classe de collection générique abstraite. Voici les classes de base ...C# - comment créer une collection générique héritée à partir d'une méthode d'usine

abstract class ItemBase { } 

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

... et leurs classes dérivées ...

class Item : ItemBase { } 

class ItemCollection : CollectionBase<Item> {} 

Maintenant, je veux une méthode de fabrication qui va créer un ItemCollection. Mais notez que les classes dérivées Item et ItemCollection sont inconnues de la classe qui contient cette méthode factory. Voilà comment j'imagine qu'il devrait être ...

static T CreateItemCollection<T>() where T : CollectionBase<ItemBase>, new() 
{ 
    return new T(); 
} 

... et je pense donc invoquer ...

var collection = CreateItemCollection<ItemCollection>(); 

Mais la méthode d'usine ne compilera pas parce que ItemBase doit avoir un constructeur sans paramètre. Et l'appel d'invocation refuse de croire que ItemCollection est dérivé de CollectionBase<ItemBase>.

Est-ce que quelqu'un peut me diriger dans la bonne direction? Merci.

Répondre

6

ItemCollectionn'est pas dérivé de CollectionBase<ItemBase>, en raison de l'invariance générique. Après tout, vous pouvez ajouter un ItemBase à un CollectionBase<ItemBase> - mais vous ne voulez pas cela pour votre ItemCollection!

Vous devez faire la méthode générique dans deux paramètres de type:

static T CreateItemCollection<TCollection, TItem>() 
    where TCollection : CollectionBase<TItem>, new() 
    where TItem : ItemBase 
{ 
    return new TCollection(); 
} 

Seul le type de collection a besoin d'un constructeur parameterless. Vous appelleriez cela avec:

var collection = CreateItemCollection<ItemCollection, Item>(); 
+0

Merci. Cela résout mon problème, même si je n'arrive toujours pas à comprendre pourquoi le compilateur insiste pour être si strict (comme l'explique JaredPar ci-dessous). –

+1

@Tim: Comme je l'ai dit, parce que ItemCollection ne doit pas permettre tous les mêmes appels que CollectionBase . Lisez la série de blog d'Eric Lippert sur la variance pour plus de détails - malheureusement je dois courir maintenant, donc je n'ai pas le temps de chasser le lien. –

3

Le problème ici est des contraintes génériques, en C# 3.0, ont une marge de manœuvre en ce qui concerne la variance. L'appariement est plutôt strict. Puisque ItemCollection dérive de CollectionBase<Item>, il n'est pas considéré être dérivé de CollectionBase<ItemBase> même si les types peuvent apparaître pour être compatibles.