2010-06-25 13 views
6

Estoy buscando formas de mejorar la coherencia, brevedad y legibilidad de algunos códigos en la aplicación en la que estoy trabajando. El código que empieza parecía algo como esto:C# Método de extensión en tipo con argumento de tipo genérico

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

Hay una serie de líneas casi idénticas de código como el anterior. Yo quería volver a escribir a ser algo como esto:

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

Por un lado, esto permitiría que tome ventaja de formalizar lo que ya se había convertido en una convención informal. Además, esperaba que ahora dijera algo así como "el bar se suscribe al evento de fizz en el contexto dado", en lugar de "el contexto tiene el tipo de barra y se suscribe a fizz y luego hace algunas cosas". Creo que el flujo es mejor, y el compañero de trabajo al que pregunté estuvo de acuerdo.

Comencé a implementar esto como un método de extensión. Para lograr lo anterior, quería hacer uso de una clase base genérica abstracta para el tipo de evento, por lo que Fizz sería Event<T>. Esto significaría que el argumento de tipo genérico para el método de extensión tendría que estar restringido para ser del tipo que se solicita el método de extensión. Entonces, para el ejemplo anterior, Fizz debería ser del tipo Event<Bar>.

¿Esto es posible? Mientras tanto, fui con una solución alternativa, pero todavía tengo curiosidad de saber si puede lograrse. Otras sugerencias son bienvenidas también.

Gracias!

Edición n. ° 1: Para que quede claro, me doy cuenta de que podría usar un parámetro de tipo adicional, pero estoy buscando la forma de evitarlo si es posible.

Edit # 2: Creo que voy a ir con una ligera variación de la respuesta aceptada, ya que no coincide al 100% con mi escenario. La conclusión es que se puede usar una clase estática genérica en lugar de un método de extensión de Tipo para lograr mi objetivo. Gracias dss539!

código de actualización (puede haber errores tipográficos ya que estoy haciendo esto sobre la marcha):

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

No entiendo muy bien su pregunta. ¿Cómo se ve la firma de su método actual? 'Suscribir (este Tipo tipo, Acción )'? Si muestra lo que tiene (o su equivalente), eso puede ayudar a explicar. – dss539

+0

Creo que tuve un problema de diseño similar hace un tiempo. Buena suerte :) – leppie

+0

@ dss539 Sería más como Suscribir (este Tipo de tipo, Contexto ctx). El problema es que no hay forma (que yo sepa) de restringir que T sea del tipo Evento . –

Respuesta

6

Esto no es exactamente como lo pediste, pero tal vez será suficiente.

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

Esto es exactamente lo que estaba buscando. Me permite lograr mi objetivo original de legibilidad, sin pasar por alto el compilador. ¡Gracias! –

+0

@Bryan - me alegro de poder ayudar :) – dss539

4

¿Por qué no hacer algo un poco más idiomáticas, donde se podía utilizar restricciones genéricas para hacer cumplir las reglas:

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

Las llamadas se vería así:

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

La restricción where TEvent : Event<TSub> asegura la relación entre el evento y el tipo de suscripción que desee. También es preferible en mi libro a un método de extensión en la clase Type, porque eso tiende a saturar intellisense. Type se usa en muchas situaciones, y tener métodos espurios en Intellisense en todas las instancias de Type tiende a ser confuso. Tampoco es obvio para los consumidores de la biblioteca que esta es la forma de "suscribirse", a menos que hayan visto un ejemplo de código.

+0

¡Gracias por su respuesta! Me doy cuenta de que la restricción se puede aplicar de esta manera (ver edición anterior). Su sugerencia es cercana a la que consideré, pero preferí el método de extensión. Entiendo la aversión a usar métodos de extensión en una clase como Type, pero el método está localizado en una sola clase que maneja las suscripciones. Además, no es un método de biblioteca que sería consumido por otros. –

0

es probable que pueda acercarse extendiéndose System.Type (tener typeof(T).) y la adición de un método (extensión) al contexto que transforma un tipo .NET para su representación de tipo interno (igual que el devuelto por 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

Hola, Mau, sucede que el método que describes ya existe. Estoy un poco confundido acerca de los parámetros de tipo en su ejemplo. ¿Quisiste decir "donde E: Evento "? Si ese fuera el caso, no necesitaría pasar un argumento de "evento", el tipo sería suficiente (de hecho, no podría proporcionar una instancia de evento). Sin embargo, parece que el tipo del suscriptor todavía tendría que ser suministrado como un parámetro de tipo. Entonces, básicamente terminarías con "typeof (Bar) .SubscribeTo (ctx)", que es otra variación que no me entusiasma. ¡Gracias por la sugerencia! –

Cuestiones relacionadas