2010-09-23 24 views
5

Tengo un objeto (formulario) que contiene una colección (.Fields) que quiero contener instancias de una clase genérica (FormField).Colección de tipos genéricos

El FormField, simplemente, se define como tal:

public class FormField<T> 
{ 
    private Form Form; 
    public T Value { get; set; } 
    public string Name { get; set; } 

    public void Process() 
    { 
     // do something 
    } 

    public FormField(Form form, string name, T value) 
    { 
     this.Name = name; 
     this.Value = value; 
     this.Form = form; 
    } 
} 

Esto me permite tener FormField, FormField etc., y esa parte funciona muy bien. Lo que yo quiero es una colección de "FormFields" sin importar el tipo, pero me veo obligado a definir un tipo (parece), tales como:

public class Form 
{ 

    string Code { get; set; } 
    string Title { get; set; } 
    int Year { get; set; } 
    Guid ClientID { get; set; } 

    ICollection<FormField<int>> Fields { get; set; } 
} 

¿Cuál es, creo, lo que quiero es una interfaz que permite me abstraer la información de tipo y por lo tanto escriba la colección como ejemplos de (por exxample) no IFormField FormField <>

Pero no puedo ver cómo definir esta fuerza sin necesidad de escribir la colección en la interfaz ...

¡Cualquier ayuda (incluidas las soluciones alternativas!) Sería muy apreciada.

Gracias, Ben

Respuesta

4

Crear una interfaz o base de clase no genérica, lo que probablemente incluye todo FormField hace excepto los bits específicos del tipo. Entonces puede tener un ICollection<IFormField>. Obviamente, no podrá usar esto de una manera fuertemente tipada, en términos del tipo de campo que se usa, pero puede usar todos los bits que no sean específicos del tipo de letra (por ejemplo, el nombre y la forma).

+1

gracias Jon, pero si te entiendo correctamente, entonces la interfaz efectivamente tendría todo excepto la propiedad de valor, que es un elemento clave de FormField ..? ¿Estoy tratando de usar genéricos de la manera incorrecta? – Ben

+1

@Ben: Considere cómo trataría de * usar * esta colección. Podría tener una mezcla de diferentes tipos de campos de formulario (int, cadena, etc.). ¿Cómo tratarías de usar los valores de esos campos de formulario dentro de 'Form'? No los sabrá en tiempo de compilación, o estará lanzando explícitamente al tipo correcto. –

+0

@Ben, puede declarar una propiedad Value en la interfaz IFormField, pero deberá declararla como Object. En la clase FormField , declare el valor como T e implemente IFormField.Value explícitamente. –

9

Aquí hay algo de código para completar la respuesta de Jon:

public interface IFormField 
{ 
    string Name { get; set; } 
    object Value { get; set; } 
} 

public class FormField<T> : IFormField 
{ 
    private Form Form; 
    public T Value { get; set; } 
    public string Name { get; set; } 

    public void Process() 
    { 
     // do something 
    } 

    public FormField(Form form, string name, T value) 
    { 
     this.Name = name; 
     this.Value = value; 
     this.Form = form; 
    } 

    // Explicit implementation of IFormField.Value 
    object IFormField.Value 
    { 
     get { return this.Value; } 
     set { this.Value = (T)value; } 
    } 
} 

Y en su formulario:

ICollection<IFormField> Fields { get; set; } 
0

Otra opción (una alternativa a Jon's answer) es aplicar la adapter pattern, que puede ser útil cuando:

  • no puede modificar ify el tipo, y por lo tanto no puede definir un tipo base para él.
  • o, hay una necesidad de exponer 'bits específicos del tipo' (como lo expresó Jon).

Cuando quiera exponer bits específicos del tipo, efectivamente have to create a non-generic wrapper. Un breve ejemplo:

class NonGenericWrapper<T> : IAdaptor 
{ 
    private readonly Adaptee<T> _adaptee; 

    public NonGenericWrapper(Adaptee<T> adaptee) 
    { 
     _adaptee = adaptee; 
    } 

    public object Value 
    { 
     get { return _adaptee.Value; } 
     set { _adaptee.Value = (T) value; } 
    } 
} 

La implementación de este comportamiento no genérico en un tipo de base se rompería con eficacia el Liskov substitution principle, que es por eso que prefiero el enfoque envoltorio como I also argue in my blog post.