2010-04-12 11 views

Répondre

0

Editer: Veuillez noter que cette réponse a été donnée avant que la question ne soit complètement changée dans une édition. Pour cette raison, il se réfère maintenant à des choses qui étaient seulement présentes dans la question comme indiqué à l'origine. Je vous demande pardon pour tous les "pointeurs qui pendent". :-)


Réponse courte:

Avec le code que vous avez posté, je ne vois pas une alternative à la coulée à IFoo<T>. Si vous ne le faites pas, le compilateur donnera un avertissement (sur ma machine, au moins).

réponse plus élaborée:

Votre code ont en fait être de cette façon? Plus précisément, avez-vous besoin de la distribution en question en premier lieu?

Je suppose que vous allez appeler votre méthode de fabrication plus ou moins comme ceci:

var stringFoo = FooFactory.CreateFoo<string>(); 

Vous devez fournir le paramètre de modèle (string dans ce cas) explicitement parce qu'il ne peut être dérivé de tout argument de méthode (dans ce cas, car il n'y en a pas du tout). Évidemment, la méthode d'usine renverra un IFoo<string>.

Maintenant, puisque vous devez spécifier explicitement le type à l'exécution, vous pouvez tout aussi bien écrire:

var stringFoo = StringFoo.Create(); 

et ont donc une méthode de fabrication à l'intérieur StringFoo, comme celui-ci, cela ne inconditionnellement l'évidence :

public class StringFoo : IFoo<string> 
{ 
    ... 

    public static StringFoo Create() // or alternatively, return an IFoo<string> 
    { 
     return new StringFoo(); 
    } 
} 

En appliquant ce modèle à d'autres implémentations trop IFoo<T>, cela vous permettra d'économiser la chaîne if ou switch bloc à l'intérieur FooFactory.CreateFoo<T>, rendre votre code plus facile, et se débarrasser de la nécessité de jeter (qui vous préoccupe). Ne vous méprenez pas, je suis conscient que les méthodes d'usine supportant plus d'un type d'objet sont utile dans certains cas; mais il semble que dans votre cas cela cause plus de problèmes que ça en vaut la peine.


P.S .: Vous trouverez peut-être un aspect de certains conteneurs IoC intéressants.Ils doivent généralement être configurés, ce qui englobe un processus dans lequel vous enregistrez des types concrets (c'est-à-dire des classes d'implémentation) pour des interfaces abstraites; par exemple (ici en utilisant Autofac):

var builder = new ContainerBuilder(); 
builder.RegisterType<StringFoo>().As<IFoo<string>>(); 

Puis, plus tard, vous pouvez demander une instance d'objet d'un type abstrait:

using (var container = builder.Build()) 
{ 
    var stringFoo = container.Resolve<IFoo<string>>(); 
    ... 
} 

La méthode Resolve est la partie intéressante. Vous lui fournissez un type abstrait, et en utilisant les types enregistrés, il retournera un objet concret de type StringFoo. Regardez-y, si cela ne vous semble pas trop compliqué! :-)

+0

Pour le downvoter: Pourquoi, si je peux demander? – stakx

+0

@Merritt, pas de soucis. Après tout, ne sommes-nous pas tous ici pour apprendre? Cela ne me dérange pas d'avoir écrit tout cela, car je pense que je peux aussi beaucoup en tirer profit en écrivant et en expliquant. – stakx

0

Pouvez-vous décrire le problème que vous résolvez avec ce mécanisme? Il y a très probablement une façon plus claire de l'aborder.

Modifier

Et oui, les odeurs code. Vous avez laissé la pièce ouverte pour n'importe quel type, sauf que vous l'obligez à revenir à un type unique et que vous générez une exception d'exécution. Pourquoi avoir un paramètre de type dans ce cas?

+3

Non, je ne le suis pas. Je ne comprends pas l'utilité d'une usine abstraite qui fournit une interface générique mais ne crée finalement qu'un type concret spécifique. Ce que vous avez décrit est une solution à un problème que vous n'avez pas encore décrit. Dites-nous quel est le problème, au lieu de votre idée préconçue sur la façon de le résoudre, et nous serons mieux en mesure de vous aider. –

+0

La technologie sous vide conduit souvent à des solutions à la recherche d'un problème. Sans définir les situations traitées par ce mécanisme, vous n'avez aucun critère sur lequel vous pouvez porter un jugement de valeur. Si votre question était plus de déterminer si cette approche est une solution viable à * certains * problème, c'est différent. Je ne vois pas personnellement une utilisation pour une fabrique générique qui n'est pas générique et qui provoque des exceptions d'exécution lorsqu'elle est utilisée en tant que telle. C'est comme fabriquer une voiture qui ne démarre pas à moins d'aller au supermarché. –

0

Vous pouvez essayer quelque chose comme ça ...

public static class FooFactory 
{ 
    private static readonly Dictionary<Type, Type> FooTypesLookup; 

    static FooFactory() 
    { 
     FooTypesLookup = (from type in typeof(FooFactory).Assembly.GetExportedTypes() 
          let fooInterface = 
          type.GetInterfaces().FirstOrDefault(
           x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IFoo<>)) 
          where fooInterface != null 
          let firstTypeArgument = fooInterface.GetGenericArguments().First() 
          select new { Type = type, TypeArgument = firstTypeArgument }) 
      .ToDictionary(x => x.TypeArgument, x => x.Type); 
    } 

    public static IFoo<T> CreateFoo<T>() 
    { 
     var genericArgumentType = typeof(T); 
     Type closedFooType; 
     return FooTypesLookup.TryGetValue(genericArgumentType, out closedFooType) 
       ? (IFoo<T>) Activator.CreateInstance(closedFooType) 
       : null; 
    } 
} 

Ou mieux encore, présenter votre conteneur IoC préféré (Windsor, carte de structure, etc.) et d'enregistrer tous les types qui mettent en œuvre IFoo là-bas et puis résolvez-les au besoin à la place de l'appel Activator.CreateInstance.

+0

L'objectif pour moi était de ne pas utiliser Activator.CreateInstance(). – Merritt