泛型使用

1.什么泛型

泛型出现的真正意义在于,类型能在运行时指定类型参数。在没有泛型的时候很多算法相同但是传入类型不同的方法需要反复的出现在不同的类里面,增加了工作量,并且在维护过程中的代价也是非常的巨大,在这样的情况下如果方法能够在运行时指定类型那么就可以降低很多的工作量,所以“懒惰”的软件工程师设计出了可以获取类型参数的方法“泛型”。

利用泛型,可以在声明变量时创建用来处理特定类型的特殊数据结构。程序员定义这种参数化类型,使特定泛型类型的每个变量都有相同的内部算法。

2.泛型的类型

在定义一个类型时,使用另一个或几个类型作为参数,类型参数用<>围住,放在所定义的类型名后面。

使用带有类型参数的泛型类型时,参数具体类型指定。

class Stack<T> {
    private T[] m_ItemArray;
}

这样定义一个泛型类型,在我们需要使用该类型的时候,我们就在实例化该类型的时候指定该对象的类型

比如:上面我们的类型我们要声明一个String类型

Stack<string> s=new Stack<string>();

这样我们就实现了在程序需要的时候动态传入需要的类型参数。

其实C#的很多类型都已经支持泛型,比如我们常用的List对象他可以动态指定类型的原因就是因为他内部是由泛型实现的。而且在.NetFramework3.5之后引入了Linq查询表达式,其中linq都有两套方法一套是普通实现另一种是泛型实现的,所以Linq中支持多种类型的查询。

又比如在一个类型中重复一个接口的实现,只要这个接口的传入类型为不一样的。

	public interface IContainer<T>
	{
		ICollection<T> Items
		{
			get;set;
		}
	}
	
	public class Person:IContainer<Address>,IContainer<Phone>
	{
		ICollection<Address> IContainer<Address>.Items
		{
			get{.....}
			set{.....}
		}
		
		ICollection<Phone> IContainer<Phone>.Items
		{
			get{.....}
			set{.....}
		}
	}

定义了一个泛型接口IContainer类型里面的属性实现也是一个泛型类型,他们同样类型来自于同一个类型。

然后再Person类型中我要实现两个Icontainer类型一个指定Address类型另一个指定为Phone类型,这样就导致不会冲突了,然后里面的属性实现也是不同的两套,这两套分别来自各自传入类型的类型。

3.泛型约束

对泛型接收参数的一些特性进行限制。泛型类中声明的每一个类型参数提供一个可选的约束条件。 使用泛型后面添加where语句限制。

3.1. 值类约束:要求泛型参数必须是值类型,例如int,short或自定义的struct

	public class MyClass<T>
				where T:struct
				{}

定义接收参数的类型如果不为struct是不会被接收的。

3.2.引用类型约束:要求泛型阐述必须是引用类型。例如string,object 和自定义class

	public class MyClass<T>
				where T:class //这个泛型类只接受引用类型的泛型参数。
				{}

定义接收参数的类型如果不为class是不会被接收的。

3.3.构造函数约束:要求泛型参数必须有构造函数

	public class MyClass<T>
				where T:new()
				{}

如果传入参数类型没有定义构造函数,类型是不能被实例化的。

3.4.接口约束:要求泛型参数必须实现某个接口

	public class MyClass<T>
				where T:System.IComparable
				{}

3.5基类约束:要求泛型参数必须继承某个基类

	public class MyClass<T>
				where T:Customer
				{}

如果没有这些约束,在使用中会因为界限不明确或条件不清楚导致一些很难调试的问题,使用约束就可以很好的界定每一个泛型可接收类型的条件并明确了这些泛型类型的使用范围。可以很好的对泛型类型的管理和界定。

4.泛型内部机制

泛型类的“类型参数”编程了元数据,“运行时”在需要的时候会利用它们构造恰当的类。所以泛型支持继承、多态以及封装。泛型类型经过编辑器编译之后生成的CIL与普通类型没什么区别的。只是泛型的CIL是参数化的,可以接收在编码由用户提供类型。以下是两份经过编辑的CIL

4.1是普通类型编译过后的CIL

.class private stack extends [mscorlib] System.Object
{
}

4.2是泛型类型编译后的CIL

.class private  'stack'1'<([mscrolib]System.IComparable) T> extends [mscorlib] System.Object
{
     .field private !0[ ] items
}

从里面我们可以看出CIL用感叹号表示法来支持泛型,表明这里可以被外部传入的类型参数所替代。


泛型显著的改变了C#1.0的编码风格,虽然Java的JVM也支持泛型,但是在运行效率上面来说JVM不如C#




泛型(Generics)是一种在编程语言中实现参数化型的技术,可以让我们编写更加灵活和通用的代码。下面是一个泛型使用案例: 假设我们有一个需求,需要实现一个通用的栈(Stack)数据结构,可以存储任何型的元素。我们可以使用泛型来实现这个通用的栈数据结构。以下是一个基于Java的示例代码: ```java public class Stack<T> { private ArrayList<T> items; public Stack() { items = new ArrayList<T>(); } public void push(T item) { items.add(item); } public T pop() { if (items.isEmpty()) { throw new RuntimeException("Stack is empty"); } return items.remove(items.size() - 1); } public boolean isEmpty() { return items.isEmpty(); } } ``` 在上面的代码中,我们使用了一个型参数 `T`,它代表任何型。我们在的定义中使用了 `<T>` 来声明这个是一个泛型,它可以接受任何型的元素。在的内部,我们使用 `T` 来代表元素的型。我们将元素存储在一个 `ArrayList<T>` 中,这个 `ArrayList` 可以存储任何型的元素。 我们定义了三个方法:`push()`、`pop()` 和 `isEmpty()`。`push()` 方法用于将元素压入栈中,`pop()` 方法用于弹出栈顶元素,并从栈中移除它,`isEmpty()` 方法用于判断栈是否为空。 使用泛型,我们可以使用这个通用的栈数据结构来存储任何型的元素,例如: ```java Stack<Integer> intStack = new Stack<Integer>(); intStack.push(1); intStack.push(2); intStack.push(3); intStack.pop(); // 返回 3 intStack.pop(); // 返回 2 Stack<String> strStack = new Stack<String>(); strStack.push("Hello"); strStack.push("World"); strStack.pop(); // 返回 "World" ``` 在上面的示例代码中,我们分别使用了 `Stack<Integer>` 和 `Stack<String>` 来存储整数和字符串型的元素。由于使用泛型,这个通用的栈数据结构可以存储任何型的元素。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Scalzdp

你的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值