2010-09-06 10 views
2

Est-il possible de construire un objet avec son constructeur interne au sein d'une méthode générique?Créer une classe générique avec constructeur interne

public abstract class FooBase { } 

public class Foo : FooBase { 
    internal Foo() { } 
} 

public static class FooFactory { 
    public static TFooResult CreateFoo<TFooResult>() 
    where TFooResult : FooBase, new() { 
     return new TFooResult(); 
    } 
} 

FooFactory réside dans le même ensemble que Foo. Les classes appellent la méthode d'usine comme ceci:

var foo = FooFactory.CreateFoo<Foo>(); 

Ils obtiennent l'erreur de compilation:

'Foo' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'TFooType' in the generic type or method 'FooFactory.CreateFoo()'

est-il un moyen de contourner ce problème?

J'ai essayé aussi:

Activator.CreateInstance<TFooResult>(); 

Cela pose la même erreur lors de l'exécution.

+0

Bizarrement, je ne reçois pas d'erreur avec votre code dans .NET 4. Je ne sais pas si cette restriction a été assouplie? Était-ce une erreur de compilation ou d'exécution? –

+0

Ah, je cours .NET 3.5. Je vais creuser. C'était une erreur de compilation. –

+0

Dans ce cas, voir ma réponse. –

Répondre

4

Vous pouvez supprimer la contrainte et le retour new():

//uses overload with non-public set to true 
(TFooResult) Activator.CreateInstance(typeof(TFooResult), true); 

bien que le client puisse le faire aussi. Ceci, cependant, est sujet aux erreurs d'exécution.

Ceci est un problème difficile à résoudre de manière sûre car la langue ne permet pas un declaraton constructeur abstrait.

+0

J'ai essayé cela - la même erreur est juste lancée à l'exécution au lieu de la compilation. –

+0

@David Neale: L'appel de la surcharge 'nonPublic' gèrera cela. Actualisé. – Ani

+0

Excellent - je n'ai pas vu cette surcharge. Merci. –

0

The type argument must have a public parameterless constructor. When used together with other constraints, the new() constraint must be specified last.

http://msdn.microsoft.com/en-us/library/d5x73970.aspx

edit: donc pas, si vous utilisez une nouvelle() contrainte, vous ne pouvez pas passer cette classe, si vous n'utilisez pas nouvelle() contrainte que vous pouvez essayer d'utiliser la réflexion pour créer une nouvelle instance

public static TFooResult CreateFoo<TFooResult>() 
where TFooResult : FooBase//, new() 
     { 
      return (TFooResult)typeof(TFooResult).GetConstructor(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance, null, new Type[] {}, null).Invoke(new object[]{}); 
      //return new TFooResult(); 
     } 
+0

Oui, merci. Je suis conscient de cela ... "Y at-il un moyen de contourner cela?" –

0

Il peut y avoir quelques contournements comme ci-dessous, mais je ne pense pas que vous voulez aller de cette façon!

  • Put instruction switch à l'intérieur de l'usine qui va créer l'instance basée sur type de paramètre de type.

  • Chaque mise en œuvre concrète de FooBase enregistrera avec FooFactory passage la méthode usine pour créer soi-même. Donc FooFactory utilisera le dictionnaire interne

  • L'extension sur la ligne similaire à l'exception du mappage entre le paramètre de type et l'implémentation concrète serait un code externe (fichier xml, configuration, etc.). Les conteneurs IOC/DI peuvent également aider ici.

0
public class GenericFactory 
{ 
    public static T Create<T>(object[] args) 
    { 
     var types = new Type[args.Length]; 
     for (var i = 0; i < args.Length; i++) 
      types[i] = args[i].GetType(); 

     return (T)typeof(T).GetConstructor(types).Invoke(args); 
    } 
}