2011-03-14 9 views
6

Tengo una función que toma un control como parámetro, y dependiendo del tipo de control (por ej .: TextBox, ComboBox, RadioButton, etc.), ejecuta type- código específico:forma más efectiva para determinar el tipo de control en C#/WPF

internal static void DoSomething(Control control) 
{ 
    if (control is Button) 
    { 
     // code for button 
    } 

    else if (control is CheckBox) 
    { 
     // code for CheckBox 
    } 

    else if (control is TextBox) 
    { 
     // code for TextBox 
    } 

    // etc..... 
} 

Me pregunto si esta es la mejor manera de hacerlo.

Conozco otras formas de hacer lo mismo (por ejemplo: buscando el tipo de control usando GetType(), activando la representación de cadena de este tipo), y la herramienta de análisis de código de Microsoft me dice que use 'como' en lugar de 'es' como esto (porque es mejor en cuanto al rendimiento):

internal static void DoSomething(Control control) 
{ 
    Button button = control as Button 
    if (button != null) 
    { 
     // code for button 
    } 

    else 
    { 
     CheckBox checkBox = control as CheckBox; 
     if (checkBox != null) 
     { 
      // code for CheckBox 
     } 

     else 
     { 
      TextBox textBox = control as TextBox; 
      if (textBox != null) 
      { 
       // code for TextBox 
      } 

      // etc..... 
     } 
    } 
} 

pero encuentro esta última solución bastante prolijo y no muy práctico para leer. Me gustaría poder activar el tipo de control directamente, pero no puedo hacerlo sin recurrir al uso de la representación de cadena (que no me gusta en absoluto) ya que la declaración de caso de un conmutador no puede contener una variable.

Entonces, ¿cuál es realmente la mejor manera de hacer rendimiento? y cuál es, en tu opinión, la mejor manera de hacerlo? (No necesariamente en cuanto al rendimiento, pero "código-lectura-sabia", por ejemplo)

Editar: como mucho está pasando sobre el tema de "¿Por qué utilizo una función común y no muchos tipo-específica métodos ", aquí hay algo más de información:

Obtengo una variable de control de otra parte de la aplicación en la que estoy trabajando (type = Control) y hago" hacer algo "con esta variable, dependiendo de su tipo.

básicamente, puedo elegir entre 2 opciones: o bien utilizo una función común y verifico el tipo de control en el cuerpo de la función para poder ejecutar la parte correcta del código en algún punto (opciones que he elegido por ahora , pero esto podría cambiar), o verifico el tipo de control ANTES de llamar a un método específico de tipo.

de cualquier manera, tengo que cambiar el tipo de control en algún momento, y ESTE es el tema de mi pregunta (independientemente de lo que hago con él, si se me permite decirlo).

Respuesta

8

me gustaría utilizar Dictionary para ello y (también métodos distintos para cada controlador):

private static readonly Dictionary<Type, Action<Control>> _handlers 
      = new Dictionary<Type, Action<Control>>(); 


// Handle.. methods 
private static void HandleButton(Button button) { ... } 
private static void HandleListbox(Listbox listbox) { ... } 

private static void RegisterHandler<T>(Action<T> handler) 
     where T: Control 
{ 
    _handlers.Add(typeof(T), o => handler((T)o)); 
} 

// invoke this method in static constructor 
private static void InitializeHandlers() 
{ 
    RegisterHandler<Button>(HandleButton); 
    RegisterHandler<Listbox>(HandleListbox); 
} 

// finally usage: 
internal static void DoSomething(Control control) 
{ 
    var handler = _handlers[control.GetType()]; 
    handler(control); 
} 

La ventaja de este enfoque es cierta mejora la mantenibilidad:
1. Usted sabrá que no está registrado, varios manejadores para el mismo tipo de parámetro (el diccionario lanzará una excepción)
2. Tendrá todos los registros de su manejador por separado, lo que le permitirá averiguar fácilmente qué método maneja un tipo de parámetro particular.
3. Dado que todo manipulador de localización de la lógica no se repite a todo lo que es bastante fácil de modificarlo con el fin de manejar tipos inhereting por ejemplo (mi código no hace esto, pero hizo su código)

+1

+1 solo para el Diccionario, no sabía nada de esto. Voy a echar un vistazo y ver si encaja. ¡Se ve bien! :) – David

+2

+1: No me gusta hacer un cambio/if-else en el tipo, se siente mal. Esto es mucho más limpio. –

+2

+1: el RegisterHandler genérico es particularmente limpio. – Gibsnag

0

Bueno, no necesita anidar en el segundo uso else if.

En segundo lugar ¿por qué estás poniendo todo esto en un método? Sería mejor ya que en el momento en que está llamando a esto, debe saber cuál es el tipo de control que está llamando a este método y, a partir de allí, solo Haga el DoSomething para ese tipo de control en lugar de toda esta comprobación condicional.

+0

Tengo un método genérico que utilizo para todos los controles, y realmente no quiero repetir el código para cada tipo de control, ya que el 99% del código de la función es común a todos los tipos (mantenimiento-pesadilla). Además, no sé el tipo de control por adelantado. Puede ser cualquier cosa y no puedo saber de antemano cuál será el tipo de control (por eso esta es una función estática que toma el control como parámetro, y no una función de instancia en la clase del control) – David

+0

"Tengo un método genérico que usar para todos los controles "- ¡no, no es así o no necesitarías saber de qué tipo son los controles! No puede ser un método ** genérico ** en cualquier caso ... – Ben

+0

ah sí, tienes razón, "genérico" no es el término que debería usar allí (pon esto en el hecho de que el inglés no es mi lengua materna) ;)). Espero que entiendas mi punto de todos modos. Añadiré información en una edición futura en mi pregunta sobre el tema. – David

0

Creo que está bien usando el operador "es" aquí. Es más legible, y realmente no tiene ninguna ruta alternativa útil en el caso de que el control no sea lo que estaba buscando de todos modos. No creo que las diferencias de tiempo sean tan críticas en este caso.

Puede cambiar el "else if" s para una serie de "si" simples al regresar de cada bloque individual, pero esa es una opción de estilo personal.

0

Sería mejor refactorizar la funcionalidad genérica (control-agnóstico) en una función separada, y tener la funcionalidad específica del control en las funciones específicas del control.

A continuación, puede llamar a la función genérica desde la función específica del control, según corresponda.

+0

Edité mi pregunta para tratar de explicar mejor mi punto. Mi problema no es la posibilidad de tener una función genérica o utilizar métodos específicos del tipo, sino más bien en el modo de activar el tipo de control – David

0

Esta es la clase de la solución que optaría por:

internal class MyClass 
{ 
    private const string ButtonTypeAsString = "Button"; 
    private const string CheckBoxTypeAsString = "CheckBox"; 
    private const string TextBoxTypeAsString = "TextBox"; 

    private static string GetTypeAsString(Control control) 
    { 
     string result = String.empty; 

     if (result.Length == 0 && (control as Button) != null) 
     { 
      result = MyClass.ButtonTypeAsString; 
     } 

     if (result.Length == 0 && (control as CheckBox) != null) 
     { 
      result = MyClass.CheckBoxTypeAsString; 
     } 

     if (result.Length == 0 && (control as TextBox) != null) 
     { 
      result = MyClass.TextBoxTypeAsString; 
     } 

     if (result.Length == 0) 
     { 
      throw new InvalidOperationException("Control type is not handled by this method."); 
     } 

     return result; 
    } 

    internal static void DoSomething(Control control) 
    { 
     string controlTypeAsString = MyClass.GetTypeAsString(control); 

     switch (controlTypeAsString) 
     { 
      case MyClass.ButtonTypeAsString: 
       // Button stuff 
       break; 

      case MyClass.CheckBoxTypeAsString: 
       // Checkbox stuff 
       break; 

      case MyClass.TextBoxTypeAsString: 
       // TextBox stuff 
       break; 

      default: 
       throw new InvalidOperationException("Unexpected Control type"); 
     } 
    } 
} 

... pero estoy seguro de que algunos encontrarían esta exageración. Personalmente, me gusta la legibilidad de la declaración switch y trato de usarla siempre que sea posible. Además, evite encender "Magic Strings". Use const strings cuando sea posible.

Si no te molesta que pregunte, ¿qué es exactamente lo que estás tratando de hacer? Puede haber una mejor solución que no implique tener que inferir el tipo de control.

Cuestiones relacionadas