泛型

一、泛型是什么

我们在编程程序时,经常会遇到功能非常相似的模块,只是它们处理的数据不一样。所以我们就写了很多个功能相同的方法。有没有一种办法,让数据和算法分离,通过传入不同类型数据去使用同一套算法呢?泛型的出现就是专门来解决这个问题的。

使用泛型可以提高代码的可重用性和灵活性。通过泛型可以编写更加通用、灵活的代码,适用于多种数据类型的情况,能更加高效地开发出健壮、可维护的应用程序。需要从如下几方面去学习泛型的使用:

  • 泛型类
  • 泛型方法
  • 泛型接口
  • 泛型委托
  • 泛型约束
     

二、泛型类(Generic Class)
 

在定义类时使用泛型,可以实现独立于数据类型的通用类。泛型类可以接收一个或多个类型参数,并在类的定义中使用这些类型参数。
下面是一个简单的示例来说明 C# 中的泛型:

using System;
// 定义一个泛型类
public class GenericClass<T>
{
	private T value;
	public GenericClass(T val)
	{
		value = val;
	}
	public void PrintValue()
	{
		Console.WriteLine("Value: " + value);
	}
}
class Program
{
	static void Main()
	{
		// 实例化一个泛型类,并指定数据类型为 int
		GenericClass<int> intClass = new GenericClass<int>(10);
		intClass.PrintValue();
		// 实例化一个泛型类,并指定数据类型为 string
		GenericClass<string> stringClass = new GenericClass<string>("Hello,Generics!");
		stringClass.PrintValue();
	}
}


在上面的示例中, GenericClass<T> 是一个泛型类,其中的 T 是类型参数。在 Main 方法中,我们分别实例化了 GenericClass<int> GenericClass<string> ,分别指定了 T 的具体类型为 int 和string ,然后调用 PrintValue 方法打印出对应的值。

通过泛型,我们可以在同一个类或方法中编写通用的逻辑,然后在实例化时根据需要指定具体的数据类型。这样能够减少代码的重复编写,提高代码的灵活性和可维护性。

三、泛型方法(Generic Method)

在 C# 中,泛型方法是一种定义可以操作任意类型数据的方法的方法。通过泛型方法,您可以编写一次代码并在需要时使用不同的数据类型,而无需为每种类型编写重复的代码。以下是一个示例来说明C#中泛型方法的用法:

using System;
public class GenericMethodDemo
{
	// 泛型方法示例
	public T FindMax<T>(T[] array) where T : IComparable<T>
	{
		if (array == null || array.Length == 0)
		{
			throw new ArgumentException("Array cannot be null or empty.");
		}		
		T max = array[0];
		foreach (T item in array)
		{
			if (item.CompareTo(max) > 0)
			{
				max = item;
			}
		}
		return max;
	}
}
class Program
{
	static void Main()
	{
		GenericMethodDemo demo = new GenericMethodDemo();
		int[] intArray = { 3, 7, 2, 5, 9 };
		float[] floatArray = { 3.5f, 7.2f, 2.8f, 5.4f };
		int maxInt = demo.FindMax(intArray);
		float maxFloat = demo.FindMax(floatArray);
		Console.WriteLine("Max integer: " + maxInt);
		Console.WriteLine("Max float: " + maxFloat);
	}
}

在上面的示例中, GenericMethodDemo 类包含了一个泛型方法 FindMax<T> 。该方法可以接受任意数据类型的数组,并返回数组中的最大值。在调用 FindMax 方法时,使用尖括号指定要操作的数据类型。在这个示例中,我们分别传递了整数数组和浮点数数组,并找到了每个数组中的最大值。

需要注意的是,在泛型方法示例中,我们使用了类型约束 where T : IComparable<T> ,确保传入的类型 T 实现了 IComparable<T> 接口以便进行比较操作。这是一个常见的泛型方法中使用的约束类型的示例。

四、泛型接口(Generic Interface)

在 C# 中,泛型接口是一种具有泛型类型参数的接口,允许在接口的定义中使用类型参数以实现通用性。通过泛型接口,可以定义一种接口结构,使得实现该接口的类可以在不同的数据类型上进行操作。以下是一个示例来说明C#中泛型接口的使用方式:

using System;
// 定义一个泛型接口
public interface ICalculator<T>
{
	T Add(T num1, T num2);
	T Subtract(T num1, T num2);
}
// 实现泛型接口的类
public class Calculator<T> : ICalculator<T>
{
	public T Add(T num1, T num2)
	{
		dynamic result = num1 + num2;
		return result;
	}

	public T Subtract(T num1, T num2)
	{
		dynamic result = num1 - num2;
		return result;
	}
}
class Program
{
	static void Main()
	{
		// 使用泛型接口的实例
		ICalculator<int> intCalculator = new Calculator<int>();
		Console.WriteLine("Addition: " + intCalculator.Add(5, 3));
		Console.WriteLine("Subtraction: " + intCalculator.Subtract(5, 3));
		ICalculator<double> doubleCalculator = new Calculator<double>();
		Console.WriteLine("Addition: " + doubleCalculator.Add(3.14, 1.5));
		Console.WriteLine("Subtraction: " + doubleCalculator.Subtract(3.14,1.5));
	}
}

在上面的示例中,我们首先定义了一个泛型接口 ICalculator<T> ,该接口包含了两个泛型方法 Add和 Subtract ,这两个方法用于执行加法和减法操作。然后我们实现了一个泛型类 Calculator<T> 来实现这个泛型接口,定义了对应的加法和减法操作。

Main 方法中,我们实例化了 Calculator<T> 类的实例,并使用 ICalculator<T> 接口来操作不同
类型的数据。通过泛型接口,我们可以轻松地对整数、浮点数等不同类型的数据进行相同类型的操作。

五、泛型委托(Generic Delegate)

在 C# 中,泛型委托是指可以操作不同类型的方法的委托。通过泛型委托,您可以定义一个可以适用于多种数据类型的委托类型,从而在需要时传入不同类型的方法进行调用。以下是一个示例来说明C#中泛型委托的使用方式:

using System;
// 定义一个泛型委托
public delegate T MathOperation<T>(T num1, T num2);
class Program
{
	static int AddInt(int a, int b)
	{
		return a + b;
	}
	static double MultiplyDouble(double a, double b)
	{
		return a * b;
	}
	static void Main()
	{
		// 使用泛型委托
		MathOperation<int> intOperation = AddInt;
		int resultInt = intOperation(5, 3);
		Console.WriteLine("Addition: " + resultInt);
		MathOperation<double> doubleOperation = MultiplyDouble;
		double resultDouble = doubleOperation(3.5, 2.0);
		Console.WriteLine("Multiplication: " + resultDouble);
	}
}

在上面的示例中,我们首先定义了一个泛型委托 MathOperation<T> ,它可以接受两个参数并返回一个类型为 T 的值。然后,我们定义了两个静态方法 AddIntMultiplyDouble ,分别用于整数和双精度浮点数的加法和乘法操作。

在 Main 方法中,我们实例化了两个泛型委托 intOperationdoubleOperation ,分别指向AddIntMultiplyDouble 方法。通过泛型委托,我们可以传递不同类型的方法给委托,然后通过委托进行调用,而无需重复定义不同类型的委托。

六、泛型约束(Generic Constraints)

在 C# 中,泛型约束用于限制泛型类型参数的行为,确保类型参数满足特定的条件或支持特定的操作。通过泛型约束,可以提高代码的可靠性和灵活性。以下是一些常见的泛型约束:

1. 类约束 ( where T : class ): 

类约束指定类型参数 T 必须是引用类型(类,接口,委托或数组)。这样可以确保泛型类型参数不是值类型。

public class MyClass<T> where T : class
{
	// T is a reference type
}

2. 结构约束 ( where T : struct ): 

结构约束要求类型参数 T 必须是结构(值类型)。

public class MyStruct<T> where T : struct
{
	// T is a value type
}

3. 无参数构造函数约束 ( where T : new() ): 

无参数构造函数约束要求类型参数 T 必须有一个公共的无参数构造函数。

public class Constructor<T> where T : new()
{
	public Constructor()
	{
		T instance = new T();
		// Use instance...
	}
}

4. 派生类约束 ( where T :基类名 ): 

派生类约束要求类型参数 T 必须是指定的基类或接口的派生
类。

public class BaseClass {}
public class DerivedClass : BaseClass {}
public class GenericClass<T> where T : BaseClass
{
	// T is a type derived from BaseClass
}

5. 界限约束 (where T :接口名 ):

界限约束可以让类型参数 T 满足指定的接口。多个接口可以使用逗号分隔。

public interface IExampleInterface {}
public class InterfaceConstraint<T> where T : IExampleInterface
{
	// T implements IExampleInterface
}

这些是一些基本的泛型约束类型,在实际开发中根据需求可以结合使用它们来限制泛型类型参数的行为,确保代码的稳健性和可靠性。

 

“您的支持是我持续分享的动力”

微信收款码
微信
支付宝收款码
支付宝


目录