2012-03-20 15 views
5

Por qué los tipos genéricos no se pueden pasar como parámetros. Con frecuencia tengo clases como:C# ¿Por qué no se pueden pasar los tipos genéricos como parámetros?

public class Example<T> where T: BaseClass 
{ 
    public int a {get; set;} 
    public List<T> mylist {get; set;} 
} 

Digamos que BaseClass es el siguiente;

public BaseClass 
{ 
    public int num; 
} 

que luego quieren un método de ejemplo:

public int MyArbitarySumMethod(Example example)//This won't compile Example not closed 
{ 
    int sum = 0; 
    foreach(BaseClass i in example.myList)//myList being infered as an IEnumerable 
     sum += i.num; 
    sum = sum * example.a; 
    return sum; 
} 

entonces tengo que escribir una interfaz sólo para pasar ésta clase como un parámetro de la siguiente manera:

public interface IExample 
{ 
public int a {get; set;} 
public IEnumerable<BaseClass> myIEnum {get;} 
} 

El genérica clase entonces tiene que ser modificado a:

public class Example<T>: IExample where T: BaseClass 
{ 
    public int a {get; set;} 
    public List<T> mylist {get; set;} 
    public IEnumerable<BaseClass> myIEnum {get {return myList;} } 
} 

Esa es una gran ceremonia por lo que pensé que el compilador podría inferir. Incluso si no se puede cambiar algo, creo que es psicológicamente muy útil si conozco las razones/justificaciones de la ausencia de atajos de sintaxis.

+3

No está claro de qué restricción está hablando, ya que no ha demostrado cómo está tratando de pasarla. I * sospecho * Tengo una idea de lo que quieres decir, pero la pregunta no está clara ... –

+0

¿Qué quieres decir cuando preguntas "[cómo] abrir un tipo genérico"? –

+0

@Rich, ¿cómo pasaría sintácticamente el 'Example ' (incluso en principio) si 'T' seguía sin especificar? –

Respuesta

2

He estado en una situación similar, pero nunca tuve esta (¡muy buena!) Pregunta. Ahora que estoy obligado a pensar en ello, aquí está mi respuesta:

se espera que el siguiente para trabajar:

void F(Example<> e) { 
    Console.WriteLine(e.a); //could work 
} 

Sí, esto podría funcionar teóricamente, pero esto no:

void F(Example<> e) { 
    Console.WriteLine(e.mylist); //?? type unknown 
} 

Se espera que el compilador y el CLR puedan analizar los cuerpos de los métodos y demostrar que realmente no es posible el acceso inseguro en el primer caso. Eso podría estar hecho para funcionar. ¿Por qué no lo fue? Probablemente, el diseño no es realmente sólido y confuso. Además, "todas las características no se implementan por defecto. Alguien debe implementarlas, probarlas y documentarlas".

Editar: Quiero señalar que esta característica no se puede implementar sin la cooperación de la CLR. El compilador de C# debe emitir el método exacto para llamar que no es posible en un tipo genérico abierto. El sistema de tipos ni siquiera permite esa variable en este momento.

+1

De hecho, todas las funciones no se implementan de manera predeterminada, pero utilizo mucho los genéricos y repetidamente me encuentro escribiendo el mismo patrón una y otra vez. Me sorprende que otros no hayan sentido una gran necesidad de esta función. Cerrar un tipo es, en esencia, una forma de herencia. –

+0

Mi experiencia me dice que esto sucede a veces, pero rara vez. Sin duda, esto depende completamente del proyecto. Pero mi punto principal es que esta característica es probablemente confusa ("¿por qué puedo acceder a una lista pero no a mi lista?", "¿Por qué funciona esto pero no eso?") Y que probablemente no sea fácil encontrar una buena solución que funcione en absolutamente todos los casos. – usr

+0

Quizás no escriba el código típico de C#. Debo confesar en las oscuras horas de la noche, los pensamientos que Smalltalk y Lisp han cruzado por mi mente. –

2

Justificación de la implementación: el soporte de esto requeriría que todos los métodos de clase genéricos no privados se hicieran virtuales, ya que de lo contrario no habría forma de saber qué método específico para solicitar un tipo genérico 'abierto'. Hay diferentes saltos de los diferentes métodos de tipo cerrado, con diferentes punteros de método correspondientes. Definir una interfaz de forma manual corresponde a instruir al compilador qué métodos deben tratarse como virtuales y también le permite especificar exactamente qué subconjunto de la funcionalidad 'abierta' desea exponer. Lo que está proponiendo es básicamente que todas las clases genéricas tengan una clase de interfaz implícitamente generada para la parte 'abierta' de su interfaz pública, lo que tiene implicaciones de rendimiento para la clase, incluso si esta característica nunca se utiliza.

+0

En realidad, la situación es peor que eso. Si un 'Foo ' tiene, por ejemplo, un campo estático 'Bar' de tipo' int', 'Foo .Bar',' Foo .Bar', 'Foo .Bar',' Foo .Bar', etc. se referirían a diferentes ubicaciones de almacenamiento que podría tener diferentes valores ¿A cuál de ellos se referiría 'Foo <>. Bar'? Dado que no hay forma general de que un compilador llame a algún miembro en un 'Foo ' podrá decir si ese miembro puede acceder directa o indirectamente a 'Foo .Bar', no tendría manera de saber si hay algún intento de invocación 'Foo <>' sin un parámetro podría ser seguro. – supercat

1

Tal vez no soy la comprensión de su pregunta exacta, pero se puede hacer que su "MyArbitrarySumMethod" Tomemos un ejemplo arbitrario declarando así:

public int MyArbitarySumMethod<T>(Example<T> example) where T : BaseClass 

entonces le puede llamar sin especificar "T":

int sum = MyArbitrarySumMethod(myExampleInstance); 

¿Eso es lo que está buscando?

+0

@KirkWoll: No, no lo creo. Este enfoque es exactamente el que iba a sugerir. –

+0

@Jon, supongo que tienes razón. Me pareció que OP estaba buscando una forma general de pasar un tipo genérico sin tener una referencia a 'T', donde la solución general * es * escribir un interfaz-wrapper para ello. Pero para el caso de uso específico de un método, tiene razón en que esto funcionará. –

Cuestiones relacionadas