什么是泛型:泛型就相当于一个模子,装入类型的材料,可以塑造成我们想要的产品。打个比方一个娃娃的模型,上面有个孔,注入金水,就是金娃娃,注入泥浆,就是泥娃娃。
T是类型,它在整个类的定义当中起到占位符的作用
怎么使用:
class Cage<T>{……} 泛型类的声明
Cage<Dog> dogCage; Cage<Dog>类型的引用
dogCage=new Cage<Dog>(); 构造实例
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var dogCage = new Cage<Dog>(1);
dogCage.PutIn(new Dog("Jack"));
dogCage.PutIn(new Dog("Herry"));
}
}
class Pet
{
private string name;
private int age;
public string Name { get => name; set => name = value; }
public int Age { get => age; set => age = value; }
public Pet(string name)
{
this.Name = name;
}
}
class Dog : Pet
{
public Dog(string name) : base(name) { }
}
class Cat : Pet
{
public Cat(string name) : base(name) { }
}
class Cage<T>
{
T[] array; // 定义一个T类型的数组,用来装宠物
private readonly int size; // 笼子里能装入的宠物总数量
private int num; // 装入笼子的宠物的数量
public Cage(int n) // 构造函数时初始化
{
size = n;
num = 0;
array = new T[size];
}
public void PutIn(T p)
{
if (num < size)
{
array[num++] = p;
}
else
Console.WriteLine("笼子满了");
}
public T TakeOut()
{
if (num>0)
{
return array[--num];
}
else
{
Console.WriteLine("笼子里没有宠物");
return default(T);
}
}
}
}
泛型方法:
泛型类中可以有泛型方法,普通类中也可以有泛型方法
语法:
class Dog{
void DogIsHappy<T>(T target){}
}
泛型参数可以用在参数列表,也可以用在返回值中,也可以在方法体中
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Dog dog = new Dog("jack");
dog.DogIsHappy<int>(7);
dog.DogIsHappy<Food>(new Food());
}
}
class Pet
{
private string name;
private int age;
public string Name { get => name; set => name = value; }
public int Age { get => age; set => age = value; }
public Pet(string name)
{
this.Name = name;
}
}
class Dog : Pet
{
public Dog(string name) : base(name) { }
public void DogIsHappy<T>(T target) {
Console.WriteLine(Name+"看见 "+target.ToString()+" 开心");
}
}
class Food {
}
}
假如我们有个需求,需要打印出传递的参数类型和参数值,我们可以这么写:
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
ShowInt(7);
ShowString("ABC");
ShowDateTime(DateTime.Now);
}
/// <summary>
/// 打印个int值
/// 因为方法声明的时候,固定了参数类型
/// </summary>
/// <param name="iParameter"></param>
public static void ShowInt(int iParameter)
{
Console.WriteLine("This is {0},parameter={1},type={2}",
typeof(Program).Name, iParameter.GetType().Name, iParameter);
}
/// <summary>
/// 打印个string值
/// </summary>
/// <param name="sParameter"></param>
public static void ShowString(string sParameter)
{
Console.WriteLine("This is {0},parameter={1},type={2}",
typeof(Program).Name, sParameter.GetType().Name, sParameter);
}
/// <summary>
/// 打印个DateTime值
/// </summary>
/// <param name="oParameter"></param>
public static void ShowDateTime(DateTime dtParameter)
{
Console.WriteLine("This is {0},parameter={1},type={2}",
typeof(Program).Name, dtParameter.GetType().Name, dtParameter);
}
}
}
可是我们发现除了传入参数不一样,其实处理方法是一样的,于是我们改成了Object类型
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Show(7);
Show("ABC");
Show(DateTime.Now);
}
public static void Show(Object iParameter)
{
Console.WriteLine("This is {0},parameter={1},type={2}",
typeof(Program).Name, iParameter.GetType().Name, iParameter);
}
}
}
这样,我们发现代码简洁了,也能达到同样的效果,但是用Object类型,涉及到装箱拆箱,装箱拆箱又涉及到会损耗程序的性能。
微软2.0推出泛型,可以很好的解决这一问题。
代码改写如下:
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Show(7);
Show("ABC");
Show(DateTime.Now);
}
public static void Show<T>(T t)
{
Console.WriteLine("This is {0},parameter={1},type={2}",
typeof(Program).Name, t.GetType().Name, t);
}
}
}
泛型接口:定义跟泛型类类似
泛型接口语法:
interface IMyself<T>
{
T GetSome(T t);
}
实现泛型接口的语法:
class A : IMyself<A>
{
public A GetSome(A t) { return default(A); }
}
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Dog dog = new Dog();
dog.study(new Sit());
}
}
interface ILearn<T> { // 定义一个学习技能的泛型接口
void study(T t);
}
class Dog : Pet,ILearn<Sit> // 狗狗类继承Pet类,并实现技能泛型接口
{
public void study(Sit t) {
Console.WriteLine("学会坐下");
}
}
class Sit { }
class Pet {
}
}
泛型的约束:缩小泛型类型的范围。我们定义了泛型,它可以传入值类型,也可以传入引用类型,范围很广。
约束的类型:类名(该类或者继承该类的类) class(任何类) struct(任何值) 接口名(该接口类型或者任何实现该接口的类型) new()(带有无参共有构造函数的类)
约束叠加规则:A主约束(类名,class,struct)只能有1个 B接口约束(任意多个) C构造约束
约束语法:
void Cage<T> where T:Pet,IclimbTree,new()
{}
这个意思就是传入一个泛型参数T,对T进行约束,它必须是Pet类或Pet的派生类,必须实现IclimbTree接口,必须要有默认构造函数
class MyClassy<T, U> where T : class where U : struct
{}
如果像上面一样,是多个泛型参数,where T 就是对T约束为class(只能输入任何类) where U 就是对U约束为struct(只能输入值类型)
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Dog dog = new Dog("jack");
//dog.DogIsHappy<int>(4); // 传入值类型错误,因为DogIsHappy限制了,只能是Pet类或继承自Pet的类
dog.DogIsHappy<Dog>(new Dog("Lucy"));
}
}
class Pet {
private string name;
public string Name { get => name; set => name = value; }
public Pet(string name)
{
Name = name;
}
public void PrintName() {
Console.WriteLine(Name);
}
}
class Dog : Pet {
public Dog(string name) : base(name) { }
public void DogIsHappy<T>(T t) where T:Pet { // 这里约束了传入的值只能是Pet类或者继承Pet的类
Console.WriteLine("happy because:"+t.Name);
}
}
}
该篇文章前面部分有句代码:return default(T)
我们现在就讲讲default关键字
default关键字可以得到该类型的默认值。
int a=0; 等价于 int a=default(int);
之所以会用到default关键字,是因为需要在不知道是值类型还是引用类型的情况下,给对象赋初值。
class TestDefault<T>
{
public T foo()
{
T t = null; //???
return t;
}
}
事实上,我们并不知道T到底是引用类型还是值类型,上面这个代码就把它当引用类型处理的,但万一T是值类型呢?例如T是int类型,那注释的那一行这么写就是无意义的,为了解决这个问题,我们就引入了default关键字。
class TestDefault<T>
{
public T foo()
{
return default(T);
}
}