2010-06-25 4 views
6

Je cherche des moyens d'améliorer la cohérence, la brièveté et la lisibilité de certains codes dans l'application sur laquelle je travaille. Le code de démarrage ressemblait à ceci:Méthode d'extension C# sur le type avec argument de type générique

context.GetGraphType<Bar>().Subscribe<Fizz>(
    (instance, evt) => e.Execute((Bar)instance.Instance) 
); 

Il existe un certain nombre de lignes de code presque identiques comme ci-dessus. Je voulais réécrire à ressembler à quelque chose comme ceci:

typeof(Bar).SubscribeTo<Fizz>(context); 

D'une part, cela me permettra de tirer profit de formaliser ce qui était déjà devenu une convention informelle. En outre, j'espérais que l'on lirait maintenant quelque chose comme "bar s'abonne à l'événement fizz sur le contexte donné", plutôt que "le contexte obtient le type de bar et souscrit à l'effervescence, puis fait quelques trucs." Je pense que le flux est mieux, et le collègue j'ai demandé à ce sujet d'accord.

J'ai commencé à implémenter cela comme une méthode d'extension. Afin d'accomplir ce qui précède, je voulais utiliser une classe de base générique abstraite pour le type d'événement, donc Fizz serait Event<T>. Cela signifierait que l'argument de type générique de la méthode d'extension devrait être contraint pour être du type auquel la méthode d'extension est appelée. Donc, pour l'exemple ci-dessus, Fizz devrait être de type Event<Bar>.

Est-ce possible? Je suis allé avec une solution alternative dans le même temps, mais je suis toujours curieux de savoir si cela peut être accompli. D'autres suggestions sont les bienvenues.

Merci! Éditer # 1: Pour être clair, je me rends compte que je pourrais utiliser un paramètre de type supplémentaire, mais je cherche des moyens d'éviter cela si possible.

Éditer # 2: Je pense que je vais aller avec une légère variation de la réponse acceptée, car elle ne correspond pas à 100% avec mon scénario. L'essentiel est qu'une classe statique générique peut être utilisée à la place d'une méthode d'extension de Type pour accomplir mon objectif. Merci dss539!

Code de mise à jour (il peut y avoir des fautes de frappe depuis que je fais ce à la volée):

public class Bar { } 

public class Event<TSubscriber> 
{ 
    public abstract void Execute(TSubscriber source); 
} 

public class Fizz : Event<Bar> 
{ 
    public override void Execute(Bar bar) 
    { 
     // respond to event 
    } 
} 

public class Context { } 

public static class ForType<TSubscriber> 
{ 
    public static void SubscribeTo<TEvent>(Context context) 
     where TEvent : Event<TSubscriber> 
    { 
     context.GetType<TSubscriber>().Subscribe<TEvent>(
      (evt, args) => evt.Execute((TSubscriber)args.Source)); 
    } 
} 

public static void Run() 
{ 
    ForType<Bar>.SubscribeTo<Fizz>(context); 
} 
+1

Je ne comprends pas très bien votre question. À quoi ressemble votre signature de méthode existante? 'S'abonner (ce type de type, action )'? Si vous montrez ce que vous avez (ou l'équivalent), cela peut aider à expliquer. – dss539

+0

Je pense avoir eu un problème de conception similaire il y a quelque temps. Bonne chance :) – leppie

+0

@ dss539 Ce serait plus comme S'abonner (ce Type de type, Contexte ctx). Le problème est qu'il n'y a aucun moyen (que je connaisse) de contraindre T à être de type Event . –

Répondre

6

Ce n'est pas exactement comme vous avez demandé, mais peut-être il suffira.

internal class Program 
{ 
    static void Main(string[] args) 
    { 
     var fizzHandler = new Fizz(); 
     var context = new Context(); 
     Handle<Bar>.With(fizzHandler, context); 
    } 
} 
public class Bar { } 
public class Event<T> { } 
public class Fizz : Event<Bar> { } 
public class Context { }; 
public static class Handle<T> 
{ 
    public static void With(Event<T> e, Context c) 
    { 
     //do your stuff 
    } 
} 
+0

C'est exactement ce que je cherchais. Cela me permet d'atteindre mon but original de lisibilité, sans contourner le compilateur. Merci! –

+0

@Brian - content de pouvoir aider :) – dss539

4

Pourquoi ne pas faire quelque chose d'un peu plus idiomatiques, où vous pouvez utiliser des contraintes génériques pour faire respecter les règles:

public static class SubscriptionManager 
{ 
    public static void SubsribeTo<TSub,TEvent>(Context context) 
     where TEvent : Event<TSub> 
    { 
     /// you code... 
    } 
} 

Les appels ressembleraient:

SubscriptionManager.SubsribeTo<Bar,Fizz>(context); 

La contrainte where TEvent : Event<TSub> assure la relation entre l'événement et le type d'abonnement que vous désirez. Il est également préférable dans mon livre à une méthode d'extension sur la classe Type - parce que cela tend à encombrer intellisense. Type est utilisé dans de nombreuses situations, et avoir des méthodes parasites apparaissent dans Intellisense sur toutes les instances de Type a tendance à être source de confusion. Il n'est pas non plus évident pour les consommateurs de bibliothèque que c'est la façon de s'abonner - à moins qu'ils n'aient vu un exemple de code.

+0

Merci pour votre réponse! Je réalise que la contrainte peut être imposée de cette manière (voir ci-dessus edit). Votre suggestion est proche de celle que j'ai considérée, mais j'ai préféré la méthode d'extension. Je comprends l'aversion à utiliser des méthodes d'extension sur une classe comme Type, mais la méthode est localisée à une seule classe qui gère les abonnements. En outre, ce n'est pas une méthode de bibliothèque qui serait consommée par d'autres. –

0

Vous pouvez probablement vous étendre près System.Type (pour avoir typeof(T).) et l'ajout d'une méthode (extension) au contexte qui transforme un type .NET à votre représentation de type interne (comme retourné par GetGraphType).

static class Ext { 

    public static TypeofTypeofBar GetGraphTypeFromDotNetType(this Context ctx, Type t) { 
     return __something(t); 
    } 

    public static void SubscribeTo<F, E>(this Type type, Context ctx, E e) 
     where E: Event<T> { 
     context.GetGraphTypeFromDotNetType(type).Subscribe<F>(a); 
    } 

} 

...

typeof(Bar).SubscribeTo(context, action); 
+0

Hey Mau, il se trouve que la méthode que vous avez décrite existe déjà. Je suis un peu confus au sujet des paramètres de type dans votre exemple. Vouliez-vous dire "où E: Event "? Si tel était le cas, je n'aurais pas besoin de passer un argument "event", le type suffirait (en fait je ne serais pas capable de fournir une instance d'événement). Cependant, il semble que le type de l'abonné doit toujours être fourni en tant que paramètre de type. Donc, vous finiriez avec "typeof (Bar) .SubscribeTo (ctx)", ce qui est encore une autre variation dont je ne suis pas ravi. Merci pour la suggestion cependant! –

Questions connexes