¿Qué son exactamente los genéricos en C#?
Genéricos: utilice tipos parametrizados para operar múltiples tipos de datos en el mismo código. Utilice "tipos parametrizados" para abstraer tipos y lograr una reutilización flexible.
Código de ejemplo:
programa de clase
{
static void Main(string[] args)
{
int obj = 2;
Prueba
Console.WriteLine("int:" + test.obj);
string obj2 = "hola mundo";
Test
Console.WriteLine("String:" + test1.obj);
Console.Read();
}
}
prueba de clase
{
public T obj;
public Test(T obj)
{
this.obj = obj;
}
}
El resultado de salida es:
int:2
Cadena:hola mundo
Análisis del programa:
1. T es el tipo genérico del que se va a crear una instancia. Si se crea una instancia de T como tipo int, entonces la variable miembro obj es de tipo int. Si se crea una instancia de T como tipo cadena, entonces obj es de tipo cadena.
2. Dependiendo del tipo, el programa anterior muestra diferentes valores.
Mecanismo genérico de C#:
Las capacidades genéricas de C# son compatibles con CLR en tiempo de ejecución: el código genérico de C# utiliza marcadores de posición especiales cuando se compila en código IL y metadatos para representar tipos genéricos y admitir operaciones genéricas. con directivas IL patentadas. El verdadero trabajo de creación de instancias genéricas se produce "bajo demanda" durante la compilación JIT.
Mire los metadatos de la función Main en el código de ahora
.method private hidebysig static void Main(string[] args) cil administrado
{ p>
.entrypoint
// Tamaño del código 79 (0x4f)
.maxstack 2
.locals init ([0] int32 obj ,
[1] clase CSharpStudy1.Test`1
[2] cadena obj2,
[3] clase CSharpStudy1.Test` 1< cadena> prueba1)
IL_0000: nop
IL_0001: ldc.i4.2
IL_0002: stloc.0
IL_0003 : ldloc .0
IL_0004: instancia de newobj clase nula CSharpStudy1.Test`1
IL_0009: stloc.1
IL_000a: ldstr "int:"
IL_000f: ldloc.1
IL_0010: ldfld !0 clase CSharpStudy1.Test`1
IL_0015: cuadro [mscorlib]System.Int32
IL_001a: cadena de llamada [mscorlib]System.String::Concat(objeto,
objeto)
IL_001f: llamada void [mscorlib]System.Console::WriteLine(string)
IL_0024: nop
IL_0025: ldstr "hola mundo"
IL_002a: stloc .2
IL_002b: ldloc.2
IL_002c: instancia newobj clase vacía CSharpStudy1.Test`1
IL_0031: stloc.3
IL_0032: ldstr "Cadena:"
IL_0037: ldloc.3
IL_0038: ldfld !0 clase CSharpStudy1.Test`1< cadena> ::obj
IL_003d: cadena de llamada [mscorlib]System.String::Concat(cadena,
string)
IL_0042: llamar void [mscorlib]System.Console::WriteLine(string)
IL_0047: nop
IL_0048: llamar int32 [mscorlib]System .Console::Read()
IL_004d: pop
IL_004e: ret
} // fin del método Program::Main
Echemos un vistazo a los metadatos del constructor en la clase de prueba
.método público hidebysig nombre especial rtspecialname
instancia void .ctor(!T obj) cil administrado
{
// Tamaño del código 17 (0x11)
.maxstack 8
IL_0000: ldarg.0
IL_0001: instancia de llamada void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: nop
IL_0008: ldarg.0
IL_0009: ldarg.1
IL_000a: stfld !0 clase ConsoleCSharpTest1.Test`1::obj
IL_000f: nop
IL_0010: ret
} // fin del método Test`1::.ctor
1 En la primera ronda de compilación, el compilador solo genera una "versión genérica" para el Test
se muestra en los metadatos del tipo de prueba 2. Durante la compilación JIT, cuando el compilador JIT encuentre Test
que se muestra en la función principal 3. CLR genera el mismo código para todos los tipos genéricos cuyos parámetros de tipo son "tipos de referencia", pero si los parámetros de tipo son "tipos de valor", cada uno; Un "tipo de valor" diferente para el cual CLR generará un código separado. Porque al crear una instancia de un tipo de referencia genérico, el tamaño asignado en la memoria es el mismo, pero al crear una instancia de un tipo de valor, el tamaño asignado en la memoria es diferente.
Características de los genéricos de C#:
1. Si los parámetros del tipo genérico instanciado son los mismos, el editor JIT reutilizará el tipo, por lo que la capacidad de genéricos dinámicos de C# evita que las plantillas estáticas de C++ puedan ser utilizadas. causar hinchazón del código.
2. Los tipos genéricos de C# contienen metadatos enriquecidos, por lo que los tipos genéricos de C# se pueden aplicar a una potente tecnología de reflexión.
3. Los genéricos de C# utilizan el método de restricción de "clase base, interfaz, constructor, tipo de valor/tipo de referencia" para implementar "restricciones de visualización" en los parámetros de tipo, lo que mejora la seguridad de los tipos. flexibilidad de las plantillas de C++ basadas en las restricciones implícitas de "firma"
Herencia genérica de C#:
Además de declarar por separado tipos genéricos (incluidas clases y estructura), también puede incluir una declaración de un tipo genérico en la clase base.
Pero si la clase base es una clase genérica, su tipo se crea una instancia o se deriva de los parámetros de tipo declarados por la subclase (también un tipo genérico), consulte los siguientes tipos
clase C
clase D:C
clase E:C
clase F:C
class G:C //Ilegal
El tipo E proporciona U y V para el tipo C, que es lo que es dicho anteriormente Se origina en subclases
El tipo F hereda de C
El tipo G es ilegal. porque el tipo G no es genérico, C es genérico y G no puede proporcionar instancias genéricas para C
Miembros de tipos genéricos:
Los miembros de tipos genéricos pueden usar parámetros de tipo genéricos en declaraciones de tipo . Pero si el parámetro de tipo no tiene ninguna restricción, solo puede utilizar miembros públicos heredados de System.Object en el tipo. Como se muestra a continuación:
Interfaz genérica:
Los parámetros de tipo de la interfaz genérica se crean instancias o se derivan de los parámetros de tipo declarados por la clase de implementación.
Genérico Delegación:
La delegación genérica admite la aplicación de tipos de parámetros en valores y parámetros de retorno delegados. Estos tipos de parámetros también pueden ir acompañados de restricciones legales
delegate bool MyDelegate
clase MiClase
{
bool estático F(int i){...}
bool estático G (cadena s ){...}
static void Main()
{
MyDelegate
MyDelegate< int> p1 = new MyDelegate
}
}
Método genérico:
1 . C# El mecanismo genérico solo admite "que contienen parámetros de tipo en declaraciones de método", es decir, métodos genéricos.
2. El mecanismo genérico de C# no admite la inclusión de parámetros de tipo en la declaración de otros miembros (incluidas propiedades, eventos, indexadores, constructores y destructores), excepto los métodos, pero estos propios miembros pueden estar contenidos en ellos. un tipo genérico y utilizando los parámetros de tipo del tipo genérico.
3. Los métodos genéricos se pueden incluir tanto en tipos genéricos como en tipos no genéricos.
Declaración de método genérico: como sigue
public static int FunctionName
Sobrecarga de métodos genéricos: p>
Función public void1
Función public void1(U a);
Esto no constituye un método genérico de sobrecarga. .
Debido a que el compilador no puede determinar si los tipos genéricos T y U son diferentes, no puede determinar si los dos métodos son diferentes
public void Function1
public void Function1(int x);
Esto puede constituir una sobrecarga
public void Function1
public void Función1
Esto no puede constituir una sobrecarga de un método genérico. Debido a que el compilador no puede determinar si A y B en las restricciones son diferentes, no puede determinar si los dos métodos son diferentes
Reescritura de métodos genéricos:
En el proceso de reescritura, las restricciones de métodos abstractos en clases abstractas se heredan de forma predeterminada. De la siguiente manera:
clase abstracta Base
{
resumen público T F resumen público T G } clase MiClase:Base { anulación pública X F anulación pública T G } Para los dos métodos anulados en MyClass El método F es legal y las restricciones se heredan de forma predeterminada El método G es ilegal, especificar cualquier restricción es redundante. Restricciones genéricas: 1. Los genéricos de C# requieren suposiciones sobre "parámetros de tipo de todos los tipos genéricos o métodos genéricos", deben basarse en "explícitos". restricciones" para mantener la seguridad de tipo requerida por C#. 2. Las "restricciones explícitas" se expresan mediante cláusulas donde, que pueden especificar "restricciones de clase base", "restricciones de interfaz", "restricciones de constructor" y "restricciones de tipo de valor/tipo de referencia"*** Cuatro limitaciones. 3. No se requieren "restricciones explícitas". Si no se especifican "restricciones explícitas", los parámetros de tipo genérico solo podrán acceder a métodos públicos en el tipo System.Object. Por ejemplo: en el ejemplo inicial, se define la variable miembro obj. Por ejemplo, agregamos una clase Test1 al ejemplo inicial y definimos dos métodos públicos Func1 y Func2 en ella, como se muestra a continuación: Restricciones de clase base: clase A { public void Func1() { } } clase B { public void Func2() { } } clase C donde S : A donde T : B { público C(S s,T t) { p> //La variable de S puede llamar al método Func1 s.Func1(); //La variable de T puede llamar al método Func2 t.Func2(); } } Restricciones de interfaz: interfaz IA { T Func1(); } interfaz IB { void Func2() ; } interfaz IC { T Func3(); } clase MiClase donde T : IA donde V : IB, IC { public MyClass(T t,V v) { //El objeto de T puede llamar a Func1 t. Func1(); //El objeto de V puede llamar a Func2 y Func3 v.Func2(); v.Func3(); p> } } Restricción del constructor: clase A { pública A () { } } clase B { público B(int i) { } } clase C { T t; público C() > { t = nueva T(); } } clase D { public void Func() { C d = new C(); } } Se produce un error cuando se compila el objeto d: El tipo B debe tener un constructor público sin parámetros para poder usarlo como parámetro 'T' en el tipo o método genérico C Nota: C# ahora solo admite restricciones de constructor sin parámetros Esto Debido a que escribimos un constructor parametrizado para el tipo B, el sistema ya no creará automáticamente un constructor sin parámetros para B. Sin embargo, si agregamos un constructor sin parámetros al tipo B, entonces la instancia del objeto d no informará un error. El tipo B se define de la siguiente manera: clase B { public B() { } public B(int i) { } } Tipo de valor/tipo de referencia: estructura pública A { } clase pública B { } clase pública C { } C c2 = new C(); El objeto c2 recibe un error al compilar: El tipo 'B' debe ser un tipo de valor que no admita valores NULL para poder usarlo como parámetro 'T' en el tipo genérico o método 'C /Static/CC/SuanFa/2010-07/157.htm p>