C# 泛型类型、泛型方法

泛型类型 Generic types

泛型会声明类型参数—泛型的消费者需要提供类型参数来把占位符类型填充

public class Stack<T>
{
	int positon;
	T[] data=new T[100];
	public void Push(T obj)=>data[position++]=obj;
	public T Pop()=>data[--position];
}

var stack=new Stack<int>();
stack.Push(2);
stack.Push(3);
int x=stack.Pop();//2
int y=stack.Pop();//3

open type & closed type

Stack Open Type(开放类型)
Stack Closed Type(封闭类型)
在运行时,所有的泛型类型实例都是封闭的(占位符已被填充了)

为什么会出现泛型

没有出现泛型之前,不同的类型实现相同的功能会写一些重复的代码。
为了解决这一点,通常会用object类型来实现一些通用的功能(因为所有的引用类型都继承自object)。但是使用object类型时,有时候会发生一些类型转化,出现装箱、拆箱的操作,损失性能,因此泛型应运而生

public class ObjectStack{
	int position;
	object[] data=new object[10];
	public void Push(object obj)=>data[position++]=obj;
	publish void Pop()=>data[--position];
}
需要装箱和向下转换,这种转化在编译时无法进行检查:
stack.Push("s");//不是object类型但可以Push进入
int i=(int)Stack.Pop();//读取时类型转换错误,编译时不会提示

stack.Push(20);//装箱操作,将值类型转化成引用类型
var value=(int)Stack.Pop();//拆箱操作,将引用类型转化为值类型

泛型方法

static void Swap<T>(ref T a,ref T b){
	T temp=a;
	a=b;
	b=temp;
}

 int x = 5;
 int y = 10;
 Swap<int>(ref x, ref y);
 //类型参数也可以省略,编译器可根据传入的参数隐式推断出类型
 //也可以写成:
 Swap(ref x,ref y);

在泛型类型里面的方法,除非也引入了类型参数,否则是不会归为泛型方法,即方法名后必须跟<T,…>才是泛型方法

//不是泛型方法,尽管参数有泛型
public void GetValue(T entity){  }

//是泛型方法
public void GetValue<T>(T entity){ }

只有类型和方法可以引入类型参数,属性、索引器、事件、字段、构造函数、操作符等不可以声明类型参数,但是他们可以使用他们所在的泛型类型的类型参数

public T this[int index] => data[index];//合法
public Stack<T>(){  }//构造函数不能引入类型参数,不合法

声明类型参数

  1. 在声明==class、struct、inteface、delegate的时候可以引入类型参数
  2. 其它的例如属性,就不可以引入类型参数,但是可以使用类型参数
public struct Nullable<T>
{
	public T Value{get;}
}

3.泛型类型/泛型方法可以有多个类型参数:

class Dictionary<TKey,TValue>{  }
var myDic=new Dictionary<int,string>();

4.泛型类型/泛型方法的名称可以被重载,条件是参数类型的个数不同:

class A { }
class A<T> { }
class A<T1,T2>{ }

按约定,泛型类型/泛型方法如果只有一个类型参数,那么就叫T。
当使用多个类型参数的时候,每个类型参数都是用T作为前缀,随后跟着具有一个描述性的一个名字。

泛型的默认值

使用default关键字来获取泛型类型参数的默认值

//T是引用类型,默认值是null
//T是值类型,默认值是0
static void Zap<T>(T[] array){
	for(int i=0;i<array.Length;i++)
	{
		array[i]=default(T);
	}
}

泛型的约束

默认情况下,泛型的类型参数可以是任何类型
如果只允许使用特定的参数类型,就可以指定约束

where T:base0-class //某个父类的子类
where T:interface //实现了某个接口
where T:class //T必须是引用类型
where T:struct //T必须是值类型
where T:new() //T必须有一个无参的构造函数
where U:T //某个类必须继承T
class SomeClass{}
interface Interface1{}

class GenericClass<T,U> where T:SomeClass,Interface1
						where U:new()
{   }

泛型约束可以作用域类型或方法的定义

public interface Icomparable<T>
{
	int CompareTo(T other);
}

//泛型约束作用于方法
static T Max<T>(T a,T b) where T:IComparable<T>
{
	return a.CompareTo(b) > 0 ? a:b;
}

struct Nullable<T> where T:struct { ...  }

static  void Initialize<T>(T[] array) where T:new()
{
	for(int i=0;i<array.Length;i++)
	{
		array[i]=new T();
	}
}

//裸类型约束
class Stack<T>
{
	Stack<U> FilteredStack<U>() where U:T{...}
}

泛型类型的子类

泛型类型class 可以有子类,在子类里,可以继续让父类的类型参数保持开放

class Stack<T> {...}
class SpecialStack<T>:Stack<T>{...}

在子类里,也可以使用具体的类型来关闭(封闭)父类的类型参数

class intStack:Stack<int>{...}

子类型也可以引入新的类型参数:

class List<T> {...}
class keyedList<T,TKey>:List<T> {...}

技术上讲,所有子类的类型参数都是新鲜的。你可以认为子类先把父类的类型参数给关闭了,然后又打开了。为这个先关闭后打开的类型参数带来新的名称或含义

class List<T> {...}
class KeyedList<TElement,TKey>:List<TElement>{...}

自引用的泛型声明

在封闭类型参数的时候,该类型可以把它自己作为具体的类型

public interface IEquatable<T> { bool Equals(T obj);}

public class Balloon:IEquatable<Balloon>
{
	public string Color{get;set;}
	public int CC {get;set;}
	public bool Equals(Balloon b)
	{
		if(b==null) return false;
		return b.Color==Color&& b.CC==CC;
	}
}

静态数据

针对每一个封闭类型,静态数据是唯一的

class Bob<T>
{
	public static int Count;
}

class Test
{
	static void Main()
	{
		Console.WriteLine(++Bob<int>.Count); //1
		Console.WriteLine(++Bob<int>.COunt);//2
		Console.WriteLine(++Bob<string>.Count);//1
		Console.WriteLine(++Bob<object>.Count);//1
	}

}

猜你喜欢

转载自blog.csdn.net/weixin_40719943/article/details/106843706