2010-05-24 20 views
0

J'ai beaucoup traité ces derniers temps avec des classes abstraites qui utilisent des génériques. Tout cela est bien et bien parce que je tire beaucoup d'utilité de ces classes, mais maintenant ça fait du code plutôt moche. Par exemple:Traiter avec plusieurs génériques dans un appel de méthode

abstract class ClassBase<T> 
{ 
    T Property { get; set; } 
} 

class MyClass : ClassBase<string> 
{ 
    OtherClass PropertyDetail { get; set; } 
} 

Cette implémentation est pas du tout fou, sauf quand je veux faire référence à la classe abstraite d'une classe d'aide et je dois faire une liste des génériques juste pour faire référence à la classe mis en œuvre , comme ci-dessous.

class Helper 
{ 
    void HelpMe<C, T>(object Value) 
     where C : ClassBase<T>, new() 
    { 
     DoWork(); 
    } 
} 

Ceci est juste un exemple apprivoisé, parce que j'ai une méthode appelle où la liste des clauses où finissent par être 5 ou 6 lignes de temps pour traiter toutes les données génériques. Ce que je voudrais vraiment faire est

class Helper 
{ 
    void HelpMe<C>(object Value) 
     where C : ClassBase, new() 
    { 
     DoWork(); 
    } 
} 

mais il ne sera évidemment pas compilé. Je veux référencer ClassBase sans avoir à lui passer tout un tableau de classes génériques pour que la fonction fonctionne, mais je ne veux pas faire référence aux classes de plus haut niveau car il y en a une douzaine. Suis-je la victime de ma propre intelligence ou existe-t-il une avenue que je n'ai pas encore considérée?

Répondre

2

Je suppose que votre méthode HelpMe serait utilisée pour initialiser le type concret ClassBase<T> (une estimation basée sur les contraintes). Pour garder le code entièrement générique (si vous avez besoin à la fois de T et de C quelque part dans la méthode), vous devez probablement conserver les deux paramètres de type.

Cependant, vous pouvez ajouter une classe de base non générique, puis écrire quelque chose comme ceci:

abstract class ClassBase { 
    object UntypedProperty { get; set; } 
} 
abstract class ClassBase<T> : ClassBase { 
    T Property { get; set; } 
    public override object UntypedProperty { 
    get { return Property; } 
    set { Property = (T)value; } 
    } 
} 

alors vous pourriez être d'écrire la méthode aide comme ceci: Selon

void HelpMe<C>(object Value) where C : ClassBase, new() { 
    var n = new C(); 
    c.UntypedProperty = Value; 
} 

votre scénario spécifique, quelque chose dans ce sens pourrait fonctionner et rendre le code un peu plus simple. Cependant, vous devez modifier la classe de base pour que cela soit possible.

0

Les génériques ont tendance à se propager à travers le code, et ils sont rarement utilisés en tant que classes "mixin" pour cette raison. Thomas mentionne la seule possibilité d'introduire une API équivalente non générique. Je préférerais revoir la conception et rendre ces classes de base non génériques si possible tout en maintenant la sécurité de type complet. Que cela soit ou non possible est déterminé par vos besoins.

Il existe une autre possibilité en dehors d'une nouvelle conception (ou d'une duplication API): dynamic. Si vous êtes prêt à perdre IntelliSense dans vos méthodes d'assistance (et sont prêts à payer une très, très petite pénalité de performance d'exécution), vous pouvez utiliser dynamique dans votre méthode d'assistance:

class Helper 
{ 
    void HelpMe<C>(object Value) 
     // where C : ClassBase<T>, new() // no longer needed, but should be documented 
    { 
     dynamic cObj = Activator.CreateInstance<C>(); // instead of "new C()" 
     cObj.PropertyDetail = ...; 
     cObj.Property = ...; 
     ... 
    } 
} 
0

sans avoir pour lui passer tout un tableau de classes génériques pour obtenir la fonction à travailler

Un petit changement pourrait faciliter ces appels. Déplacer plusieurs fois les types spécifiés dans la déclaration générique de la classe.

//before 
Helper x = new Helper(); 
x.HelpMe<MyClass, string>(x); 
x.HelpMe<MyClass, string>(y); 

    //after 
Helper x = new Helper<string>(); 
x.HelpMe<MyClass>(x); 
x.HelpMe<MyClass>(y); 



    //the change 
class Helper<T> 
{ 
    void HelpMe<C>(object Value) 
     where C : ClassBase<T>, new() 
    { 
     DoWork(); 
    } 
}