2009-07-28 9 views
5

Estoy escribiendo una aplicación web .NET en la que los administradores pueden personalizar los diversos formularios de entrada de datos presentados a sus usuarios. Hay aproximadamente media docena de tipos de campos diferentes que los administradores pueden crear y personalizar (por ejemplo, texto, numérico, menú desplegable, carga de archivos). Todos los campos comparten un conjunto de atributos/comportamientos básicos (¿se requiere el campo? ¿Tendrá un valor de campo predeterminado?). También hay una serie de atributos/comportamientos específicos de campo (es decir, el menú desplegable tiene un atributo de fuente de datos, pero el campo de texto no). Estoy dejando de lado muchas otras características del dominio del problema por simplicidad.método de sobrecarga y polimorfismo

La jerarquía de clases es simple: una superclase abstracta que encapsula comportamientos/atributos comunes y alrededor de media docena de subclases concretas que se ocupan de cosas específicas de campo.

Cada tipo de campo se procesa (es decir, se asigna a) como un tipo específico de control de servidor .NET, todos los cuales se derivan de System.Web.UI.Control.

creé el código siguiente para asignar los valores de los objetos de dominio entre el campo y su control de interfaz de usuario correspondiente:

public static void Bind(Control control, IList<DocumentFieldBase> fieldBaseList) 

    foreach (DocumentFieldBase fieldBase in fields){ 

      if (typeof (DocumentFieldText).IsInstanceOfType(fieldBase)){ 
       TextBox textbox = (TextBox) control; 
       textbox.Text = (fieldBase as DocumentFieldText).GetValue(); 
      } 

      if (typeof (DocumentFieldDropDown).IsInstanceOfType(fieldBase)){ 
       DropDown dropDown= (DropDown) control; 
       dropDown.Text = (fieldBase as DocumentFieldSelectOne).GetValue().Text; 
       dropDown.DataSource= (fieldBase as DocumentFieldSelectOne).DataSource; 
       dropDown.Id= (fieldBase as DocumentFieldSelectOne).GetValue().Id; 
      } 

      //more if statements left out for brevity 
     } 
} 

quiero para deshacerse de los malos si las declaraciones que realizan la comprobación de tipos. El enfoque para el que estaba filmando fue crear una sobrecarga de método para cada combinación de campo/control utilizando la tipificación de subclase. Por ejemplo:

public static void Bind(TextBox control, DocumentFieldText fieldText){ 
//some implementation code 
} 
public static void Bind(DropDown control, DocumentFieldDropDown fieldDropDown){ 
//some implementation code 
} 

Yo esperaba que entonces podría depender de .NET para llamar a la sobrecarga apropiada en tiempo de ejecución usando la subclase específica que se utiliza: Por ejemplo:

foreach (DocumentFieldBase field in fields){ 
    Control control = FindControl(field.Identifier); 
    Bind(control, field) 
} 

Por desgracia, la el compilador se ahoga cuando intento esto: Argumento '1': no ​​se puede convertir de 'System.Web.UI.Control' a 'TextBox'.

Si tengo que lanzar el primer argumento a TextBox, he vuelto a realizar una comprobación de tipo yo mismo y he derrotado el propósito de este ejercicio.

Es lo que estoy tratando de lograr a) posible yb) una buena idea?

Respuesta

5

La etiqueta "dispatch" en esta pregunta es bastante apropiada: lo que quiere se llama "envío múltiple". C# (como la mayoría de los lenguajes principales) solo es compatible con "envío único", donde el método que se ejecutará se selecciona únicamente en el tipo (tiempo de ejecución) del objeto al que llama el método, no en el tipo (tiempo de ejecución) de sus argumentos.

El patrón de visitantes a menudo se puede utilizar para evitar este. La idea es que usted da DocumentFieldBase un método (que se anula en las subclases concretas) que llama a un método en Control (también se reemplaza en subclases concretas) que hace el trabajo real.

Desafortunadamente, el código fuente de la clase Control probablemente no esté bajo su control * ... así que tendrá que recurrir a una solución aún más hackosa. La respuesta aceptada al this question proporciona una que usa la reflexión.

* Los métodos de extensión son simplemente azúcar sintáctica para los métodos estáticos, y por lo tanto se resuelven en tiempo de compilación y no sirve de nada en este escenario.

+0

Respuesta otorgada porque le pusiste un nombre a mi dolor :) A menudo he leído sobre el doble despacho y el patrón Visitor y tenía la corazonada de que ese era el problema (de ahí la etiqueta de "despacho" que mencionas). También ha logrado destilar las diferencias cruciales entre el despacho único y el despacho múltiple, algo que muchos de los artículos que he leído sobre el tema no pudieron hacer. –

6

Antes de C# 4, todas las sobrecargas se realizan en tiempo de compilación. Debe utilizar el envío doble o el patrón de visitante para sobrecargar eficazmente el tiempo de ejecución, y eso se complica rápidamente.

En C# 4, se puede declarar una variable como dinámico y dejar que todo solucionarse en tiempo de ejecución:

foreach (DocumentFieldBase field in fields){ 
    dynamic control = FindControl(field.Identifier); 
    Bind(control, field) 
} 

Obviamente eso no es de mucha ayuda en este momento, aunque (a menos que estés usando VS2010b1) .

Una opción es usar un mapa de Type a Action<object> pero luego tienes problemas de herencia ... (es posible que tengas que seguir trabajando en la jerarquía de tipos desde el tipo concreto hasta el objeto hasta que encuentres una entrada en el mapa). También necesitaría lanzar al tipo correcto dentro de la acción :(

0

¿No podría tener un método abstracto, no estático Bind() en DocumentFieldBase, y luego hacer el downcasting dentro de cada implementación de la clase concreta de la misma? Cada clase de DocumentFieldBase sabe qué tipo de Control está recibiendo, ¿no es así?

+0

Carl, que no tuvo en cuenta este enfoque. No estoy muy interesado en introducir una dependencia en System.Web.UI en mi capa de dominio, pero puedo escribir una rápida de POC de todos modos. ¡Gracias! –

Cuestiones relacionadas