2009-03-13 15 views
13

¿Alguien puede explicar este comportamiento en Generics?C# Generics function

que tienen una función genérica en C#

protected virtual void LoadFieldDataEditor <T> (ref T control, string strFieldName) where T : Control 
{ 
    //T can be different types of controls inheriting from System.Web.UI.Control 
    if (control is TextBox) 
    { 
    //This line gives an error 
    //((TextBox)control).Text = "test"; 

    //This line works! 
    (control as TextBox).Text = "Test"; 
    } 
} 

En una nota lateral, puedo usar el caso del interruptor cuando estoy haciendo un "control de cuadro de texto es" tipo de cheques?

EDIT:

olvidó añadir el mensaje de error Lo sentimos!

Aquí van:

Error 3 Cannot convert type 'T' to 'TextBox' 

EDIT:

Si bien estamos hablando de los genéricos, tengo otra pregunta. (No estaba seguro si tuviera que empezar una nueva entrada)

El método se ha ampliado para incluir otro tipo genérico

protected virtual void LoadFieldDataEditor <T1, T2> (T1 control, T2 objData, string strFieldName) where T1 : Control where T2 : BaseDataType 
{ 
    //I will need to access field1. 
    //I don't know at compile time if this would be SomeType1 or 
//SomeType2 but all of them inherit from BaseDataType. 

    //Is this possible using generics? 
} 

public abstract class BaseDataType {} 

public class SomeType1 : BaseDataType 
{ 
    string field1; 
    string field2; 
} 
+0

Cuando pregunta qué significa un error, es realmente útil decir cuál es el error. Solo estoy tratando de reproducir ahora ... –

+0

¿Cuál es el error? –

+0

@Jon Skeet: Maldita sea ... No me di cuenta de que estabas en la caja ... –

Respuesta

21

Las reglas para lo que es un tipo genérico se puede convertir en son bastante complicado, y de vez en cuando contraintuitivo, como en este caso. Ver la sección 6.2.6 de la especificación C# para más detalles. Hay lugares donde podrían ser más relajados, y creo que este es uno de ellos. Usted puede emitir hasta object y luego de nuevo, pero eso es feo.

En este caso, la mejor solución sería:

protected virtual void LoadFieldDataEditor <T> (ref T control, 
               string strFieldName) 
    where T : Control 
{ 
    TextBox textBox = control as TextBox; 
    if (textBox != null) 
    { 
     textBox.Text = "test"; 
    } 
} 

Aparte de cualquier otra cosa, esto sólo requiere un solo cheque el tiempo de ejecución en lugar de dos.

Para la nota al margen: no, no puede usar el interruptor/caja en los tipos. (Puede obtener el nombre del tipo y activarlo, pero sería horrible.)

+0

Está creando un nuevo cuadro de texto en este método. ¿Cambiará el texto en este cambio el texto en el que se envió como parámetro (ref Control)? – DotnetDude

+0

No, este método no crea un nuevo TextBox. ¿Qué línea crees que crea una nueva? Sospecho fuertemente que en realidad no necesitas pasar el control ref ... –

+0

Pensé que esta línea - TextBox textBox = control como TextBox; creó un nuevo cuadro de texto. Podría tener que repasar mi "pase por val" y pasar por ref conceptos. Tengo entendido que cuando pasa por valor (es decir, sin ref) crea una nueva var en una nueva ubicación de memoria y copia el valor de origen – DotnetDude

1

La primera línea da el error del compilador: "No se puede convertir el tipo T en TextBox". Ese tipo de elenco solo es legal si el compilador puede saber que es posible convertir la clase inicial en la clase final. Debido a que T podría ser cualquier cosa, no hay forma de que el compilador lo sepa. Aunque está comprobando en tiempo de ejecución, esto no apacigua al compilador. El segundo tipo de elenco está bien, ya que simplemente devolverá nulo si el elenco no funciona. EDITAR: Como señala Tuinstoel, las reglas para el casting son más complicadas de lo que describí.

+0

Ver mi respuesta. – tuinstoel

0

Respondida a @rossfabricant.

No es tan simple, el primer método se compila, el segundo no.

void Test(Control control) 
{ 
    if (control is TextBox) 
    { 
     ((TextBox)control).Text = "test"; 
    } 
} 

void Test<T>(T control) where T : Control 
{ 
    if (control is TextBox) 
    { 
     ((TextBox)control).Text = "test"; 
    } 
} 
3

me gustaría altamente recomiendan refactorización que esto sea:

protected virtual void LoadFieldDataEditor(Control control, string strFieldName) 

Como se ha mencionado en algunos comentarios, este método no necesita genéricos en absoluto.

Dado que está restringiendo a un Control, conoce la clase base, y es un tipo de referencia (Control), por lo que puede evitar los genéricos y la declaración del parámetro ref.

Dado que Control es un tipo de referencia, puede cambiar sus propiedades en el método, y esto funcionará correctamente. Configurar .Text, etc. hará exactamente lo que intenta hacer, pero será mucho más simple.

Hay una pequeña posibilidad de que pudiera necesita que sea:

protected virtual void LoadFieldDataEditor(ref Control control, string strFieldName) 

pero esto sólo sería necesaria si se va a reasignar el control dentro de su método (es decir: control = new TextBox();). Recomiendo encarecidamente no hacerlo, ya que puede causar un comportamiento muy inesperado y no sería obvio. Si está intentando crear un nuevo control, usar un parámetro out o simplemente devolver el nuevo control lo haría todo mucho más claro.

Además, en general, es una buena idea evitar los métodos genéricos a menos que haya una buena razón para incluirlos. El equipo FxCop ha agregado (y luego eliminado) algunas reglas que intentan desalentar su uso porque tienden a hacer que el código sea menos comprensible a largo plazo. Hay muchas buenas razones para usar métodos genéricos, pero esto no requiere su uso, por lo que recomendaría evitarlos.

+0

Gracias por la explicación. Asignar texto solo fue para hacer la pregunta. En realidad, tengo muchas otras cosas sucediendo. Pero sí, me refactoré para no usar ref y ¡funciona genial! Gracias – DotnetDude