2010-01-06 4 views
2

Je dois avoir le paramètre de type générique en tant qu'interface, mais je voudrais instancier le type dans la classe générique (SomeGenericType) comme suit:Problèmes avec les types abstraits et les interafaces comme paramètres de type génériques avec la contrainte new()

class Program 
{ 
    static void Main(string[] args) 
    { 
     var val = new SomeGenericType<ISomeInterface>(); 

     Console.ReadKey(); 
    } 
} 

internal class SomeGenericType<T> where T : new() 
{ 
    public SomeGenericType() 
    { 
     var test = new T(); 
    } 
} 

public class SomeClass : ISomeInterface 
{ 
    public string TestVal { get; set; } 
} 

public interface ISomeInterface 
{ 
    string TestVal { get; set; } 
} 

Cela jette l'erreur de compilation suivante:

« ISomeInterface doit être un type non abstrait avec un constructeur parameterless public afin de l'utiliser comme paramètre « T » dans le type générique ou méthode SomeGenericType »

Je comprends pourquoi cela se produit, mais je me demandais s'il y avait moyen de contourner ce problème?

Merci.

+0

L'ajout d'une contrainte supplémentaire serait-il utile? 'où T: ISomeInterface, new()'. Vous allez devoir fournir une implémentation concrète au générique, comme vous l'avez mentionné, car il n'y a pas d'autre moyen de garantir la contrainte 'new()'. – Sapph

+1

Si vous avez Interface1, Class1: Interface1, Class2: Interface1, comment le compilateur doit-il deviner si vous voulez créer une instance de Class1 ou Class2? –

Répondre

1

Le problème est que le compilateur n'a aucun moyen de savoir quelle classe instancier pour l'interface donnée. Comme David M souligne:

This is the sort of thing that an Inversion of Control (IOC) container can handle for you

Je pense que l'aide d'un cadre pourrait être plus tuer pour cette exigence simple. Ce que vous pouvez faire est de créer une classe Factory de votre propre comme ceci:

public class Factory 
{ 
    Dictionary<Type, Type> typeMapping = new Dictionary<Type, Type>(); 

    public void Register<IType, CType>() 
    { 
    typeMapping.Add(typeof(IType),typeof(CType)); 
    } 

    public IType Create<IType>() 
    { 
    Activator.CreateInstance(typeMapping[typeof(IType)]); 
    } 
} 

jeter dans quelques contrôles de santé mentale et cette classe devrait être prête à l'emploi.

6

Non, la contrainte new() exige qu'une instance du type peut être créé avec la syntaxe

new T() 

Ceci est clairement vrai ni une classe abstraite ou une interface, seule une classe concrète avec constructeur public sans paramètre.

Vous pouvez reporter le problème jusqu'à ce que l'exécution en supprimant la contrainte et l'utilisation:

Activator.CreateInstance<T>() 

au lieu de créer l'objet. Ensuite, tant que le type utilisé lors de l'exécution satisfait ces contraintes, votre code fonctionnera comme vous le souhaitez. Cependant, si vous essayez d'utiliser une interface ou une classe abstraite, vous rencontrerez une erreur d'exécution.

Dans votre cas, cette ligne jetterait une exception

var val = Activator.CreateInstance<SomeGenericType<ISomeInterface>>(); 

Vous êtes passé l'erreur de compilation, mais sans effet.

1

Le problème ici est que la contrainte new est liée à une implémentation de type concret. Cela ne peut jamais fonctionner avec simplement une interface et une classe abstraite puisqu'elles ne peuvent pas être directement instanciées. Vous devez fournir une classe concrète ici

var val = new SomeGenericType<SomeClass>() 
2

Une autre idée, qui peut être hors de propos, mais il semble que vous êtes à la recherche d'un moyen de demander une ISomeInterface, et ont une instance de sa mise en œuvre « par défaut » SomeClass à condition de. C'est le genre de chose qu'un conteneur Inversion of Control (IOC) peut gérer pour vous. Si vous souhaitez approfondir vos recherches, vous pouvez consulter Spring.NET, Microsoft Unity, AutoFac, LinFu ou l'un des nombreux autres frameworks.

Questions connexes