2010-08-19 6 views
2

J'ai un ensemble de classes (MyClass1, MyClass2, MyClass3) qui implémentent une interface (IMyClass).C# Collection utilisant les types génériques ou l'interface

Je suis intéressé par la création d'une méthode pour remplir et retourner une liste (Collection.Generics.Lists<>) des objets qui ont une interface commune MyClassX (List<IMyClass>). Mais comment?

Chaque classe a un constructeur MyClassX qui fait quelque chose de différent. Donc, je ne peux pas créer un BaseClass et utiliser un constructeur commun pour tous.

par l'interface Je ne peux pas faire:

public List<IMyClass> Fill(string[] ItemsPassedByParam, IMyClass InstanceOfMyClassX) 
    List MyList = new List<IMyClass>(); 
    foreach (item in ItemsPassedByParam) 
    { 
     MyList.Add (new IMyClass (item)); //can't do new from an Interface!! 
    } 
} 

Aussi j'ai essayé d'utiliser le type de médicaments génériques, TMyClass dans l'exemple suivant, mais ni a fonctionné pour moi (même si je n'ai pas des notions très profondes sur les types génériques):

public List<TMyClass> Fill(string[] ItemsPassedByParam, Type TypeOfMyClassX) 
    List MyList = new List <TMyClass>(); 
    foreach (item in ItemsPassedByParam) 
    { 
     MyList.Add (new TMyClass (item)); //can't do new from a Generic Type!! 
    } 
} 

J'ai essayé aussi d'extérioriser le code dans le constructeur de MyClassX dans une méthode, mais je ne peux pas appeler la méthode depuis de l'objet n'est pas initialisé (il n'y a pas new()).

Dans ces cas, quelle est la solution?

Merci d'avance!

+0

Qu'est-ce qu'un 'item'? Est-ce qu'il identifie le type? – cjk

+0

En outre, ce qui est "nouvelle liste ();" censé être? Il n'y a rien à propos de cela qui pourrait compiler. –

Répondre

7

On dirait que vous voulez que la méthode de remplissage pour créer un List<T> du passé dans les valeurs de chaîne où T est un dérivation de IMyClass.

Ce que vous devez faire est de passer une fonction d'usine dans la méthode de remplissage qui permet la création d'instances dérivées IMyClass.

public List<IMyClass> Fill(
    string[] ItemsPassedByParam, 
    Func<string,IMyClass> createFunc) 

    List MyList = new List<IMyClass>(); 
    foreach (item in ItemsPassedByParam) { 
    MyList.Add(createFunc(item));   
    } 
} 

ensuite à l'endroit que vous appelez Remplissez vous pouvez choisir la dérivation de IMyClass à utiliser

string[] items = ...; 
List<IMyClass> list1 = Fill(items, item => new MyClass1(item)); 
List<IMyClass> list2 = Fill(items, item => new MyClass2(item)); 
+0

Merci Jared! J'ai fait un mélange entre les vôtres et les solutions de Justin Niessner. Parce que rappeler au temps de développement sur la sorcière est le constructeur à utiliser n'est pas très pratique pour moi. J'ai donc fait un 'MyClassFactory.CreateInstance()' et aussi un 'MyClassFactory.GetConstrucor (Type)' pour obtenir le constructeur approprié. – Alex

2

La meilleure option ici est de créer un IMyClassFactory qui gère en prenant le paramètre, déterminer quel type de béton pour construire, appeler le constructeur, et le retourner:

public class IMyClassFactory 
{ 
    public static IMyClass CreateInstance(string item) 
    { 
     switch(item) 
     { 
      case "SomeValue": 
      case "SomeValue2": 
       return new MyClass1(item); 
      case "SomeOtherValue": 
       return new MyClass2(item); 
     } 
    } 
} 

Cette usine suppose évidemment que vous pouvez déduire tapez de la chaîne transmise à l'usine. Il y a de fortes chances que ce ne soit pas le cas, auquel cas vous devez ajouter un autre paramètre qui vous permet d'inférer le type concret à instancier.

Une fois que vous avez l'usine, vous pouvez rendre votre méthode originale d'utiliser l'usine:

public List<IMyClass> Fill(string[] ItemsPassedByParam) 
{ 
    List MyList = new List<IMyClass>(); 

    foreach (item in ItemsPassedByParam) 
    { 
     MyList.Add(IMyClassFactory.CreateInstance(item)); 
    } 
} 
+1

IMyClassFactory? Avec un "je"? Cette convention de nommage mènera à la confusion ... –

+0

Merci Justin! Après votre chemin, je construit le vôtre 'MyClassFactory.CreateInstance()' et 'a également fait une MyClassFactory.GetConstrucor (type t)' utiliser la solution Jared . – Alex

1

Peut-être que vous pouvez simplement utiliser le modèle d'usine ici, selon la façon dont vous jugez quel type est nécessaire.Peut-être que vous aurez besoin d'utiliser la réflexion et invoquer un ConstructorInfo (passe des paramètres à un constructeur, retourne un objet, bon pour construire dans les méthodes de modèle, mais aussi bon pour exploser en cours d'exécution quand vous faites l'équivalent de quelque chose qui serait attrapé à la compilation).

Ce dernier semble le plus proche de ce que vous avez essayé, mais si vous pouvez juste faire les choses d'une usine de plus de type sécurisé alors c'est probablement le chemin à parcourir.

public static T BuildOne(SomeParamType myParam) 
{ 
    ConstructorInfo constrInfo = typeof(T).GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, new Type[]{typeof(SomeParamType)}, null); 
    return (T)constrInfo.Invoke(new object[]{myParam}); 
} 
2

Vous ne pouvez pas créer une instance d'une interface, mais vous pouvez créer une instance d'un type générique - à condition qu'il ait un constructeur par défaut. vous pouvez ensuite passer l'élément à l'instance à une propriété ou une méthode plutôt que d'utiliser l'injection de constructeur.

public List<TMyClass> Fill(string[] ItemsPassedByParam) 
    where TMyClass : IMyClass, new() { 
    ... 
     IMyClass inst = new TMyClass(); 
     inst.SetItem(item); 
     myList.Add(inst); 
    ... 
} 

Appel new TMyClass a le même effet que l'appel Activator.CreateInstance<TMyClass>() Le CreateInstance générique ne permet pas d'arguments à passer au constructeur, mais vous pouvez les passer avec un CreateInstance non genric.

public List<TMyClass> Fill(string[] ItemsPassedByParam) { 
    ... 
     myList.Add((IMyList)Activator.CreateInstance(typeof(TMyClass), item)); 
    ... 
}