2012-03-12 19 views
9

Quiero tener una función de impresión genérica ... PrintGeneric (T) ... en el siguiente caso, ¿qué me estoy perdiendo?C#: Paso de un objeto genérico

Como siempre su ayuda/penetración se aprecia ...

public interface ITest 
{} 

public class MyClass1 : ITest 
{ 
    public string myvar = "hello 1"; 
} 

public class MyClass2 : ITest 
{ 
    public string myvar = "hello 2"; 
} 

class DoSomethingClass 
{ 

    static void Main() 
    { 
     MyClass1 test1 = new MyClass1(); 
     MyClass2 test2 = new MyClass2(); 

     Console.WriteLine(test1.myvar); 
     Console.WriteLine(test2.myvar);    
     Console.WriteLine(test1.GetType()); 

     PrintGeneric(test1); 
     PrintGeneric<test2.GetType()>(test2); 
    } 

    // following doesn't compile 
    public void PrintGeneric<T>(T test) 
    { 
     Console.WriteLine("Generic : " + test.myvar); 
    } 
} 
+1

'var' no puede ser un nombre de variable, ya que es una palabra clave. ¿Puedes mostrar tu código actual? –

+3

@AshBurlaczenko No es correcto. 'var' es una palabra clave contextual agregada en C# 3.0. Puede usarlo como un nombre de identificador. Del mismo modo, puede usar 'yield',' select', 'from', etc. como nombres de identificadores en la mayoría de los contextos. –

+0

realmente "var" funcionó sin usar Console.WriteLine ... pero fue una mala idea usar una palabra clave como nombre de identificador ... – user1229895

Respuesta

19

No se compila porque T podría ser cualquier cosa, y no todo tendrá el campo myvar.

Se podría hacer myvar una propiedad en ITest:

public ITest 
{ 
    string myvar{get;} 
} 

y poner en práctica en las clases como una propiedad:

public class MyClass1 : ITest 
{ 
    public string myvar{ get { return "hello 1"; } } 
} 

y luego poner una limitación genérica de su método:

public void PrintGeneric<T>(T test) where T : ITest 
{ 
    Console.WriteLine("Generic : " + test.myvar); 
} 

pero en ese caso, para ser sincero, es mejor que ju st pasar en un ITest:

public void PrintGeneric(ITest test) 
{ 
    Console.WriteLine("Generic : " + test.myvar); 
} 
+0

Agregué una propiedad a ITest, pero entonces me dice que necesito implementarlo en MyClass1/MyClass2 ... lo cual tiene sentido, pero luego aparece un error que dice que ya contiene una definición para myvar – user1229895

+0

Oh sí, entonces necesitas cambiar los campos para que sean propiedades en el implementando clases. Actualicé mi respuesta para mostrarte. – jonnystoten

+0

probé PrintGeneric (test2) en Main para el genérico y obtuve una 'Asignación, llamada, incremento, decremento utilizado como una declaración' y PrintGeneric (test1) para PrintGeneric (prueba ITest). .y obtener un "Se requiere una referencia de objeto para el campo no estático" – user1229895

0

tratar

public void PrintGeneric<T>(T test) where T: ITest 
{ 
    Console.WriteLine("Generic : " + [email protected]); 
} 

como @Ash Burlaczenko ha dicho que no puedo nombrar a una variable después de una palabra clave, si desea que esta reallllly prefijo con símbolo @ para escapar de la palabra clave

+1

Esto no funcionará, 'ITest' no tiene un miembro llamado' var'. –

+0

Solo voy a trabajar si ITest especifica la propiedad var, por supuesto. No estoy seguro de que lo haga en el ejemplo. –

+0

sí a la derecha esto tiene que agregarán al ITest así –

2

En su método genérico, T es simplemente un marcador de posición para un tipo. Sin embargo, el compilador no sabe nada sobre los tipos concretos que se utilizan en tiempo de ejecución, por lo que no puede suponer que tendrán un miembro var.

La forma habitual de eludir esto es agregar una restricción de tipo genérico a su declaración de método para asegurarse de que los tipos utilizados implementar una interfaz específica (en su caso, podría ser ITest):

public void PrintGeneric<T>(T test) where T : ITest 

Entonces , los miembros de esa interfaz estarían directamente disponibles dentro del método. Sin embargo, su ITest está actualmente vacío, necesita declarar cosas comunes allí para permitir su uso dentro del método.

2

Vas a tener que dar más información acerca del tipo genérico T. En su método actual PrintGeneric, T bien podría ser un string, que no tiene un miembro var.

es posible que desee cambiar var a una propiedad en lugar de un campo

public interface ITest 
{ 
    string var { get; } 
} 

Y agregar una restricción where T: ITest al método PrintGeneric.

6

Que se está perdiendo al menos un par de cosas:

  • A menos que estés utilizando la reflexión, los argumentos de tipo tienen que ser conocido en tiempo de compilación, por lo que no se puede utilizar

    PrintGeneric<test2.GetType()> 
    

    ... aunque en este caso no es necesario de todos modos

  • PrintGeneric no se sabe nada acerca de T en este momento, por lo que el compilador de C An't encontrar un miembro llama T

Opciones:

  • Deja una propiedad en la interfaz ITest, y cambiar PrintGeneric para limitar T:

    public void PrintGeneric<T>(T test) where T : ITest 
    { 
        Console.WriteLine("Generic : " + test.PropertyFromInterface); 
    } 
    
  • Deja una propiedad en la interfaz ITest y elimine los genéricos por completo:

    public void PrintGeneric(ITest test) 
    { 
        Console.WriteLine("Property : " + test.PropertyFromInterface); 
    } 
    
  • Uso tipado dinámico en lugar de los genéricos si estás usando C# 4

+1

Typo en su segundo fragmento de código Jon? Dijiste eliminar genéricos pero todavía hay un en el método? –

+2

@SteveHaigh: Doh, eso es lo que obtengo por tipear y hablar por teléfono al mismo tiempo. Reparado, gracias. –

+0

probando la primera opción ... Agrego "string myvar {get;}" a ITest ... pero obtengo un error que dice MyClass1/MyClass2 no implementa el miembro de la interfaz 'ITest.myvar' ... agregando "string myvar" {get;} "a MyClass1/MyClass2 produce un error de compilación diciendo que ya está definido? – user1229895

1

Es necesario definir algo en la interfaz, tales como:

public interface ITest 
{ 
    string Name { get; } 
} 

Implemente ITest en su c lasses:

public class MyClass1 : ITest 
{ 
    public string Name { get { return "Test1"; } } 
} 

public class MyClass2 : ITest 
{ 
    public string Name { get { return "Test2"; } } 
} 

Entonces restringir su función genérica Print, a ITest:

public void Print<T>(T test) where T : ITest 
{ 
} 
+0

Ver mi respuesta en el comentario de Ash a la pregunta. Puede usar 'var' como nombre de identificador, al igual que' yield', etc., aunque probablemente no sea una gran idea. Es una palabra clave contextual Pruebe 'int var;' para verificarlo usted mismo. –

+0

@MehrdadAfshari, ah ... No estaba al tanto de eso. Bueno, hoy aprendí. Pero sí, no es algo que realmente haría en realidad :) –

Cuestiones relacionadas