CSharp (C #) Language_Advanced (colección genérica)

Que es genérico

  La característica genérica proporciona una forma más elegante de permitir que varios tipos compartan un conjunto de código. Permítanos declarar parámetros genéricos del código tipo (tipo parametrizado), pueden ser instancias de diferentes tipos. En otras palabras, podemos usar "marcadores de posición de tipo" para escribir código y luego especificar el tipo real al crear una instancia de la clase.

Genéricos en C #

  • C # proporciona 5 tipos genéricos: clase, estructura, interfaz, delegado y método.Los primeros cuatro son tipos y los métodos son miembros
    Los tipos genéricos son plantillas para tipos
    Los tipos genéricos son plantillas para tipos Tipos
    Tipos genéricos y definidos por el usuario
    genéricos y definidos por el usuario

Clase genérica

  Las clases genéricas no son clases reales, sino plantillas de clases , por lo que primero debemos construir los tipos de clases reales a partir de ellas y luego crear una instancia de este tipo de clase construida.

  • Use marcadores de posición en ciertos tipos para declarar una clase
  • Proporcione el tipo real para el marcador de posición. De esta forma, existe una definición de la clase real, que llena todas las "vacantes". Este tipo se llama tipo construido (tipo construido)
  • Crea instancias de tipos construidos

Crea una instancia a partir de un tipo genérico
Crea una instancia a partir de un tipo genérico

Declarar una clase genérica

Declarar una clase genérica simple es similar a declarar una clase normal

Las diferencias son las siguientes:

  • Coloque un conjunto de corchetes angulares después del nombre de la clase
  • Cadenas de marcadores de posición separadas por comas entre paréntesis angulares para indicar el tipo deseado. Esto se llama parámetro de tipo (parámetro typc)
  • Use parámetros de tipo en el cuerpo de una declaración de clase genérica para indicar el tipo que debe reemplazarse
//				  类型参数
class SomeClass < T1, T2 >
{
    
    
	// T1、T2表示在当前位置使用类型
	public T1 SomeVar = new T1();
	public T2 OtherVar = new T2();
}

Crear tipo de construcción

  Una vez que se crea el tipo genérico, necesitamos decirle al compilador qué tipos reales se pueden usar en lugar de marcadores de posición (parámetros de tipo). El compilador obtiene estos tipos reales y crea un tipo construido (una plantilla utilizada para crear un objeto de clase real)
  . La sintaxis para crear un tipo construido es la siguiente, incluida la lista del nombre de la clase y proporcionando el tipo real entre paréntesis angulares en lugar de parámetros de tipo. El tipo real para reemplazar el parámetro de tipo se llama argumento de tipo (typeargument) La diferencia entre el
SomeClass < short, int >Inserte la descripción de la imagen aquí
parámetro de tipo y el argumento de tipo (abajo)
Inserte la descripción de la imagen aquí

Crea variables e instancias

MyNonGenClass 			 myNGC = new MyNonGenClass			 ();
//		构造类								 构造类
SomeClass < short, int > mySc1 = new SomeClass < short, int >();
var 					 mySc2 = new SomeClass < short, int >();

PD:Se pueden construir muchos tipos de clases diferentes a partir de la misma clase genérica. Cada uno tiene un tipo de clase separado, como si todos tuvieran declaraciones no genéricas independientes

La diferencia entre la pila no genérica y la pila genérica

- No genérico Genérico
Tamaño del código fuente Más grande: necesitamos escribir una nueva implementación para cada tipo Más pequeño: independientemente del número de tipos de construcción, solo necesitamos una implementación
Tamaño ejecutable Independientemente de si se utilizará cada versión de la pila, aparecerá en la versión compilada Solo los tipos con tipos estructurales aparecen en archivos ejecutables
Facilidad de escritura Fácil de escribir porque es más específico Más difícil de escribir porque es más abstracto
Facilidad de mantenimiento Más propenso a problemas, porque todas las modificaciones deben aplicarse a todos los tipos disponibles Fácil de mantener, porque solo es necesario modificar un lugar

Tipo de restricciones de parámetro

Las restricciones se enumeran mediante la cláusula where

  • Cada parámetro de tipo restringido tiene su propia cláusula where
  • Si el parámetro tiene varias restricciones, están separadas por comas en la cláusula where.
    where 参数类型 : 约束1, 约束2, ...

Los puntos principales de la cláusula where:

  • Se enumeran después del corchete angular de cierre en la lista de parámetros de tipo
  • No están separados por comas u otros símbolos.
  • Se pueden enumerar en cualquier orden.
  • donde es una palabra clave contextual, por lo que se puede utilizar en otros contextos

Tipo y orden de restricción

Nombre de la clase Solo las clases de este tipo o las clases heredadas de él pueden usarse como argumentos de tipo
clase Cualquier tipo de referencia, incluidas clases, matrices, delegados e interfaces se puede utilizar como argumentos de tipo.
estructura Cualquier tipo de valor se puede utilizar como argumento de tipo
Nombre de la interfaz Solo esta interfaz o el tipo que implementa esta interfaz se puede utilizar como argumento de tipo
nuevo() Cualquier tipo con un constructor público sin parámetros se puede utilizar como argumentos de tipo. Esto se llama restricción de constructor.

ps: La cláusula where se puede enumerar en cualquier orden. Sin embargo, las restricciones en la cláusula where deben tener un orden específico

  • Puede haber como máximo una restricción primaria, si hay una, debe colocarse primero
  • Puede haber cualquier número de restricciones de nombre de interfaz
  • Si hay una restricción de constructor, debe colocarse en último lugar

Método genérico

  A diferencia de otros genéricos, los métodos son miembros, no tipos. Los métodos genéricos pueden ser estructuras genéricas y no genéricas y declaraciones de interfaces
Los métodos genéricos se pueden declarar en tipos no genéricos de tipos genéricos
, un método genérico
  con una lista de parámetros de tipo opcional y un método genérico de restricción.

  • El método genérico tiene dos listas de parámetros
    • Lista de parámetros del método entre paréntesis
    • Lista de parámetros de tipo entre paréntesis angulares
  • Para declarar un método genérico, necesita:
    • Coloque la lista de parámetros de tipo después del nombre del método y antes de los parámetros del método
    • Coloque cláusulas de restricción opcionales después de la lista de parámetros del método

p.ej:

public void PrintData <S, T>(string str, int t) where S:Person
{
    
    
	...
}

Llamar a un método genérico
  Para llamar a un método genérico, se debe proporcionar el argumento de tipo cuando se llama al método
  MyMethod <short,int>();

Ejemplos de métodos genéricos

class Simple 										// 非泛型类
{
    
    
	static public void ReverseAndPrint<T>(T[] arr)  // 泛型方法
	{
    
    
		Array.Reverse(arr);
		foreach (T iten in arr) 					// 使用类型参T
		Console.krite("(0),", item.ToString());
		Console.Mriteline(*);
	}
}

class Program
{
    
    
	static void Kain()
	{
    
    
		//创建各种类型的数糕
		var intArray … new int[] {
    
    3, 5, 7, 9, 11 };
		var stringArray - new string[] {
    
     "first", "second", "third" };
		var doubleArray * nex double[]{
    
     3.567, 7.891, 2.345 };
		
		Simple.ReverseAndPrintcint>(intArray); 		 // 调用方法
		Simple.ReverseAndPrint(intArray); 			 // 推断类型并调用
		
		Simple.ReverseAndPrint<string>(stringArray); // 调用方法
		Simple.ReverseAndPrint(stringArray); 		 // 推断类型并调用
		
		Simple.ReverseAndPrintcdouble>(doubleArray); // 调用方法
		Simple.ReverseAndPrint(doubleArray); 		 // 推断类型并调用
	}
}

El resultado del caso anterior:

  11, 9, 7, 5, 3
  3, 5, 7, 9, 11

  tercero, segundo, primero
  primero, segundo, tercero

  2.345, 7.891
  , 3.567 3.567, 7.891, 2.345

Métodos de extensión y clases genéricas

Métodos de extensión de clases genéricas :

  • Debe declararse como estático
  • Debe ser miembro de una clase estática
  • Debe haber la palabra clave this en el primer tipo de parámetro, seguida del nombre de la clase genérica extendida

Caso

static class ExtendHolder
{
    
    
	public static void Print<T>(this Holder<T> h)
	{
    
    
		T[] vals= h.GetValues();
 		Console.NriteLine("{0},\t{1},\t{2}",vals[0], vals[1],vals[2]);
 	}
 }
 
class Holder<T>
{
    
    
	T[] Vals = new T[3];
	
	public Holder(T V0, T V1,T v2)
	{
    
    
		vals[0] = v0;
		Vals[1] = v1;
		Vals[2] = v2;
	}
	public T[] GetValues() 
	{
    
     
		return Vals;
	}
	
class Program
{
    
    
	static void Main(string[] args) 
	{
    
    
		var intHolder = new Holder<int>(3, 5, 7);
		var stringHolder = new Holder<string>("a1", "b2", "c3");
		intHolder.Print();
		stringHolder.Print();
	}
}

Resultado del caso anterior:

3, 5, 7
a1, b2, c3

Estructura genérica

  • Similar a las clases genéricas, la estructura genérica puede tener parámetros y restricciones de tipo
  • Las reglas y condiciones de la estructura del paradigma son las mismas que las de la clase genérica

Caso

struct PieceOfData<T> //泛型结构
{
    
    
	public PieceOfData(T value)
	{
    
     
		_data = value;
	}
	
	private T _data;
	public T Data
	{
    
    
		set _data = value;
	{
    
    
}

class Program
{
    
    
	static void Main() 
	{
    
    
							  // 构造类型
		var intData = new Piece0fData<int>(10);
								//		构造类型
		var stringData = new PieceOfData<string>("Hi there.");

		Console.WriteLine("intData = {0}", intData.Data);
		Console.WriteLine("stringData = {0}", stringData.Data);
	}
}

Resultado del caso:

intData = 10
stringData = Hola

Delegado genérico

  • Los delegados genéricos y los delegados no genéricos son muy similares, pero el parámetro de tipo determina qué métodos pueden aceptarse
    • Para declarar un delegado genérico, coloque la lista de parámetros de tipo entre paréntesis angulares después del nombre del delegado y antes de la lista de parámetros del delegado

Caso

delegate void MyDelegate<T>(T value); 				// 泛型委托
class Simple
{
    
    
	static public void PrintString(string s) 		// 方法匹配委托
	{
    
    
		Console.WriteLine(s);
	}
	static public void PrintUpperString(string s) 	// 方法匹配委托
	{
    
    
		Console.MriteLine("(O)",s.ToUpper());
	}
}

class Program
{
    
    
	static void Main()
	{
    
    
		//创建委托的实例
		var myDel = new MyDelegate<string>(Simple.PrintString);
		//添加方法
		myDel += Simple.PrintUpperString; 
		//调用委托
		myDel("Hi There.");
	}
}

Resultado del caso:

Hola,
Hola, Hola

Interfaz genérica

  Las interfaces genéricas nos permiten escribir interfaces cuyos parámetros y tipos de retorno de miembros de interfaz son parámetros de tipo genérico. La declaración de una interfaz genérica es similar a la declaración de una interfaz no genérica, pero los parámetros de tipo deben colocarse entre paréntesis angulares después del nombre de la interfaz

Caso

interface IMyIfc<T>	// 泛型接口
{
    
    
	T RetrnIt( T inValue);
}

//      类型参数  泛型接口
class Simple<S> :IMyIfc<S> // 泛型类
{
    
     
	public S ReturnIt(S inValue) //实现泛型接口
	{
    
     
		return inValue
	}
}

class Program
{
    
    
	static void Main()
	{
    
    
		var trivInt = new Simple<int>();
		var trivString = new Simple<string>();

		Console.WriteLine("{0)",trivInt.ReturnIt(5));
		Console.NriteLine("{o)",trivString.ReturnIt("Hi there."));
	}
}

Resultado del caso:

5
Hola.

ps: la implementación de la interfaz genérica debe ser única

Supongo que te gusta

Origin blog.csdn.net/qq_43562262/article/details/106019344
Recomendado
Clasificación