7.泛型-泛型约束
7.1 知识点
知识回顾
泛型类
// 泛型类
class GenericClass<T, U>
{
// 泛型变量
public T t;
public U u;
// 泛型类中的成员方法
public U GenericClassFunction(T t)
{
return default(U);
}
// 泛型类中的泛型方法
public V GenericClassFunction<K, V>(K k)
{
return default(V);
}
}
主函数内
// 泛型类,实际使用时再具体传入类型
GenericClass<string, int> genericClass = new GenericClass<string, int>();
// 泛型变量
genericClass.t = "123123";
genericClass.u = 10;
// 泛型类中的成员方法
genericClass.GenericClassFunction("123");
// 泛型类中的泛型方法
genericClass.GenericClassFunction<float, double>(1.4f);
什么是泛型约束
- 泛型约束:让泛型的类型有一定限制。
- 关键字:
where
。 - 语法:
where 泛型字母: (约束的类型)
。
泛型约束分类
- 值类型:
where 泛型字母: struct
- 引用类型:
where 泛型字母: class
- 存在无参公共构造函数:
where 泛型字母: new()
- 某个类本身或者其派生类:
where 泛型字母: 类名
- 某个接口的派生类型:
where 泛型字母: 接口名
- 另一个泛型类型本身或者派生类型:
where 泛型字母: 另一个泛型字母
各泛型约束讲解
值类型约束
// 值类型泛型约束类,代表 T 一定是一个结构体
class Test1<T> where T : struct
{
// 泛型变量
public T value;
// 值类型泛型约束方法,代表 K 一定是一个结构体
public void TestFun<K>(K v) where K : struct
{
}
}
// int 实际上也是一个结构体,可以传入值类型泛型约束类的实例中
Test1<int> t1 = new Test1<int>();
// float 实际上也是一个结构体,可以传入值类型泛型约束方法中
t1.TestFun<float>(1.3f);
引用类型约束
// 引用类型泛型约束类,代表 T 一定是一个类
class Test2<T> where T : class
{
// 泛型变量
public T value;
// 引用类型泛型约束方法,代表 K 一定是一个类
public void TestFun<K>(K k) where K : class
{
}
}
// Random 实际上是一个类,可以传入引用类型泛型约束类的实例中
Test2<Random> t2 = new Test2<Random>();
// 这样 value 的类型也是 Random
t2.value = new Random();
// object 实际上也是一个类,可以传入引用类型泛型约束方法中
t2.TestFun<object>(new object());
公共无参构造约束
// 公共无参构造泛型约束类,代表 T 一定要有公共无参构造函数
class Test3<T> where T : new()
{
// 泛型变量
public T value;
// 公共无参构造泛型约束方法,代表 K 一定要有公共无参构造函数
public void TestFun<K>(K k) where K : new()
{
}
}
// Test31 有公共无参构造,正常 new 出来
Test3<Test31> t31 = new Test3<Test31>();
// Test32 没有公共无参构造,报错
// Test3<Test32> t32 = new Test3<Test32>();
// Test33 只有私有无参构造,报错
// Test3<Test33> t33 = new Test3<Test33>();
// Test34 有公共无参构造,但是 Test34 是抽象类,无法直接用无参构造 new 出来,报错
// Test3<Test34> t34 = new Test3<Test34>();
// 可以传入结构体,因为结构体默认有公共无参构造,而且就算写了有参构造,公共无参构造也不会被顶掉
Test3<int> t3Struct = new Test3<int>();
类约束
// 类约束泛型约束类,代表 T 一定是 Test31 或 Test31 的派生类
class Test4<T> where T : Test31
{
// 泛型变量
public T value;
// 类约束泛型约束方法,代表 K 一定是 Test31 或 Test31 的派生类
public void TestFun<K>(K k) where K : Test31
{
}
}
// 直接传入 Test31,正常 new
Test4<Test31> t4 = new Test4<Test31>();
// Test41 继承 Test31,正常 new
Test4<Test41> t41 = new Test4<Test41>();
// Test42 没有继承 Test31,报错
// Test4<Test42> t42 = new Test4<Test42>();
// object 是 Test31 的父类,不是 Test31 的派生类,报错
// Test4<object> t4Object = new Test4<object>();
接口约束
// 接口约束泛型约束类,代表 T 一定是 IFly 或 IFly 的派生类
class Test5<T> where T : IFly
{
// 泛型变量
public T value;
// 接口约束泛型约束方法,代表 T 一定是 IFly 或 IFly 的派生类
public void TestFun<K>(K k) where K : IFly
{
}
}
// 飞行接口
interface IFly
{
}
// 走路接口
interface IWalk
{
}
// 移动接口继承飞行接口和走路接口
interface IMove : IFly, IWalk
{
}
// 我的飞行类,继承飞行接口
class MyFly : IFly
{
}
// 直接传入 IFly,正常 new
Test5<IFly> t51 = new Test5<IFly>();
t51.value = new MyFly(); // MyFly 继承 IFly,可以 new 出接口的实现子类
// IWalk 没有继承 IFly,报错
// Test5<IWalk> t52 = new Test5<IWalk>();
// IMove 继承 IFly,正常 new
Test5<IMove> t53 = new Test5<IMove>();
// MyFly 继承 IFly,正常 new
Test5<MyFly> t54 = new Test5<MyFly>();
另一个泛型约束
// 另一个泛型约束泛型约束类,代表 T 一定是 U 或 U 的派生类
class Test6<T, U> where T : U
{
// 泛型变量
public T value;
// 另一个泛型约束泛型约束类,代表 K 一定是 V 或 V 的派生类
public void TestFun<K, V>(K k) where K : V
{
}
}
// MyFly 是 IFly 的子类,正常 new
Test6<MyFly, IFly> t61 = new Test6<MyFly, IFly>();
// MyFly 不是 IWalk 的子类,报错
// Test6<MyFly, IWalk> t62 = new Test6<MyFly, IWalk>();
约束的组合使用
// 语法:class 类<T> where T: 约束1,约束2
// 意味着 T 必须是个类,而且有公共的无参构造函数
// 有些约束只能现在另一个约束前面,比如 class 要写在 new() 前面,否则报错
class Test71<T> where T : class, new()
{
}
// 有些组合不能一起使用,会报错
// 比如 struct 本身就有公共的无参构造函数,不能组合
// class Test72<T> where T : struct, new()
// {
//
// }
多个泛型有约束
// 语法:class 类<T,K> where T: 约束1,约束2 where K:约束3
class Test8<T, K> where T : class, new() where K : struct
{
}
总结
- 泛型约束:让泛型的类型有一定限制。
- 泛型约束分类:
- class
- struct
- new()
- 类名
- 接口名
- 另一个泛型字母
- 注意:
- 可以组合使用。
- 多个泛型约束用
where
连接即可。
7.2 知识点代码
using System;
namespace Lesson6_泛型约束
{
//class语句块外 namespace语句块内
#region 知识回顾
//泛型类
class GenericClass<T, U>
{
//泛型变量
public T t;
public U u;
//泛型类中的成员方法
public U GenericClassFunction(T t)
{
return default(U);
}
//泛型类中的泛型方法
public V GenericClassFunction<K, V>(K k)
{
return default(V);
}
}
#endregion
#region 知识点一 什么是泛型约束
//泛型约束:让泛型的类型有一定的限制
//关键字:where
//语法:where 泛型字母:(约束的类型)
//泛型约束一共有6种
//1.值类型 where 泛型字母:struct
//2.引用类型 where 泛型字母:class
//3.存在无参公共构造函数 where 泛型字母:new()
//4.某个类本身或者其派生类 where 泛型字母:类名
//5.某个接口的派生类型 where 泛型字母:接口名
//6.另一个泛型类型本身或者派生类型 where 泛型字母:另一个泛型字母
#endregion
#region 知识点二 各泛型约束讲解
#region 值类型约束
//值类型泛型约束类 代表T一定是一个结构体
class Test1<T> where T : struct
{
//泛型变量
public T value;
//值类型泛型约束方法 代表K一定是一个结构体
public void TestFun<K>(K v) where K : struct
{
}
}
#endregion
#region 引用类型约束
//引用类型泛型约束类 代表T一定是一个类
class Test2<T> where T : class
{
//泛型变量
public T value;
//引用类型泛型约束方法 代表K一定是一个结构体
public void TestFun<K>(K k) where K : class
{
}
}
#endregion
#region 公共无参构造约束
//公共无参构造泛型约束类 代表T一定要有公共无参构造函数
class Test3<T> where T : new()
{
//泛型变量
public T value;
//公共无参构造泛型约束方法 代表K一定要有公共无参构造函数
public void TestFun<K>(K k) where K : new()
{
}
}
//测试类 有公共无参构造
class Test31
{
public Test31()
{
}
}
//测试类 没有公共无参构造
class Test32
{
public Test32(int a)
{
}
}
//测试类 有私有无参构造
class Test33
{
private Test33(int a)
{
}
}
//抽象测试类 有公共无参构造
abstract class Test34
{
public Test34(int a)
{
}
}
#endregion
#region 类约束
//类约束泛型约束类 代表T一定是Test31或Test31的派生类
class Test4<T> where T : Test31
{
//泛型变量
public T value;
//类约束泛型约束方法 代表K一定是Test31或Test31的派生类
public void TestFun<K>(K k) where K : Test31
{
}
}
//Test41继承Test31
class Test41 : Test31
{
}
//Test42没有继承Test31
class Test42
{
}
#endregion
#region 接口约束
//接口约束泛型约束类 代表T一定是IFly或IFly的派生类
class Test5<T> where T : IFly
{
//泛型变量
public T value;
//接口约束泛型约束方法 代表T一定是IFly或IFly的派生类
public void TestFun<K>(K k) where K : IFly
{
}
}
//飞行接口
interface IFly
{
}
//走路接口
interface IWalk
{
}
//移动接口继承飞行接口和走路接口
interface IMove : IFly, IWalk
{
}
//我的飞行类 继承飞行接口
class MyFly : IFly
{
}
#endregion
#region 另一个泛型约束
//另一个泛型约束泛型约束类 代表T一定是U或U的派生类
class Test6<T, U> where T : U
{
//泛型变量
public T value;
//另一个泛型约束泛型约束类 代表K一定是V或V的派生类
public void TestFun<K, V>(K k) where K : V
{
}
}
#endregion
#endregion
#region 知识点三 约束的组合使用
//语法:class 类<T> where T: 约束1,约束2
//意味着T必须是个类 而且有公共的无参构造函数
//有些约束只能现在另一个约束前面 比如class要写在new()前面 否则报错
class Test71<T> where T : class, new()
{
}
//有些组合不能一起使用 会报错
//比如struct本身就有公共的无参构造函数 不能组合
//class Test72<T> where T : struct, new()
//{
//}
#endregion
#region 知识点四 多个泛型有约束
//语法:class 类<T,K> where T: 约束1,约束2 where K:约束3
class Test8<T, K> where T : class, new() where K : struct
{
}
#endregion
#region 总结
//泛型约束:让泛型的类型有一定限制
//泛型约束分类:
//class
//struct
//new()
//类名
//接口名
//另一个泛型字母
//注意:
//1.可以组合使用
//2.多个泛型约束 用where连接即可
#endregion
class Program
{
static void Main(string[] args)
{
Console.WriteLine("泛型约束");
//主函数内
#region 知识回顾
//泛型类 实际使用的时候再具体传入类型
GenericClass<string, int> genericClass = new GenericClass<string, int>();
//泛型变量
genericClass.t = "1`23123";
genericClass.u = 10;
//泛型类中的成员方法
genericClass.GenericClassFunction("123");
//泛型类中的泛型方法
genericClass.GenericClassFunction<float, double>(1.4f);
#endregion
#region 知识点二 各泛型约束讲解
#region 值类型约束
//int实际上也是一个结构体 可以传入值类型泛型约束类的实例中
Test1<int> t1 = new Test1<int>();
//float实际上也是一个结构体 可以传入值类型泛型约束方法中
t1.TestFun<float>(1.3f);
//Test31<object> t11 = new Test31<object>();//报错 值类型泛型约束类不能传入引用类型
#endregion
#region 引用类型约束
//Random实际上是一个类 可以传入引用类型泛型约束类的实例中
Test2<Random> t2 = new Test2<Random>();
//这样value的类型也是Random
t2.value = new Random();
//object实际上也是一个结构体 可以传入引用类型泛型约束方法中
t2.TestFun<object>(new object());
//Test32<int> t11 = new Test32<int>();//报错 引用类型泛型约束类不能传入值类型
#endregion
#region 公共无参构造约束
//Test31有公共无参构造 正常new出来
Test3<Test31> t31 = new Test3<Test31>();
//Test32没有公共无参构造 报错
//Test41<Test32> t32 = new Test41<Test32>();
//Test33只有私有无参构造 报错
//Test3<Test33> t33 = new Test3<Test33>();
//Test34有公共无参构造 但是Test34是抽象类 无法直接用无参构造new出来 所以其实他的公共无参构造没用 报错
//Test3<Test34> t34 = new Test3<Test34>();
//可以传入结构体 因为结构体默认有公共无参构造 而且就算写了有参构造 公共无参构造也不会被顶掉
Test3<int> t3Struct = new Test3<int>();
#endregion
#region 类约束
//直接传入Test31 正常new
Test4<Test31> t4 = new Test4<Test31>();
//Test41继承Test31 正常new
Test4<Test41> t41 = new Test4<Test41>();
//Test42没有继承Test31 报错
//MyFly<Test42> t42 = new MyFly<Test42>();
//object是Test31的父类 不是Test31的派生类 报错
//MyFly<object> t4Object = new MyFly<object>();
#endregion
#region 接口约束
//直接传入IFly 正常new
Test5<IFly> t51 = new Test5<IFly>();
//t51.value = new IFly();//报错 接口不能new
t51.value = new MyFly();//MyFly继承IFly 可以new出接口的实现子类
//IWalk没有继承IFly 报错
//Test5<IWalk> t52 = new Test5<IWalk>();
//IMove继承IFly 正常new
Test5<IMove> t53 = new Test5<IMove>();
//MyFly继承IFly 正常new
Test5<MyFly> t54 = new Test5<MyFly>();
#endregion
#region 另一个泛型约束
//MyFly是IFly的子类 正常new
Test6<MyFly, IFly> t61 = new Test6<MyFly, IFly>();
//MyFly不是IWalk的子类 报错
//Test6<MyFly, IWalk> t62 = new Test6<MyFly, IWalk>();
#endregion
#endregion
}
}
}
7.3 练习题
用泛型实现一个单例模式基类
class语句块外 namespace语句块内
// 泛型单例基类,泛型约束一下,必须要有公共无参构造
// 缺点:有无参构造暴露给外部使用,不能私有,但其实无伤大雅,有经验的程序员不会new单例类
class SingleBase<T> where T : new()
{
private static T instance = new T();
public static T Instance
{
get
{
return instance;
}
}
}
// 游戏管理类,继承泛型单例基类
class GameMgr : SingleBase<GameMgr>
{
public int value = 10;
}
// 测试类1,继承泛型单例基类
class Test : SingleBase<Test>
{
public int value = 10;
}
// 测试类2,不继承泛型单例基类,要自己手动写单例实现
class Test2
{
private static Test2 instance = new Test2();
public int value = 10;
private Test2()
{
}
public static Test2 Instance
{
get
{
return instance;
}
}
}
主函数内
GameMgr.Instance.value = 10; // 直接.Instance使用单例
// GameMgr g = new GameMgr(); // 缺点:有无参构造暴露给外部使用,不能私有,但其实无伤大雅,有经验的程序员不会new单例类
Test.Instance.value = 10; // 直接.Instance使用单例
// Test t = new Test(); // 缺点:有无参构造暴露给外部使用,不能私有,但其实无伤大雅,有经验的程序员不会new单例类
Test2.Instance.value = 10; // 直接.Instance使用单例,但是是自己手动实现的,没有继承泛型单例基类
利用泛型知识点,仿造ArrayList实现一个不确定数组类型的类,实现增删查改方法
class语句块外 namespace语句块内
// 我们自己写的泛型ArrayList类
// 和C#自带的ArrayList相比,我们自己写的可以约束类型,不能什么类都往里面加,不用装箱拆箱
class ArrayList<T>
{
// 泛型数组
private T[] array;
// 当前正儿八经存了多少数
private int count;
// 构造函数
public ArrayList()
{
// 默认一一开始存的数为0
count = 0;
// 一开始的容量就是16
array = new T[16];
}
// 增
public void Add(T value)
{
// 是否需要扩容
if (count >= Capacity)
{
// 每次加倍扩容
T[] temp = new T[Capacity * 2];
// 遍历搬家
for (int i = 0; i < Capacity; i++)
{
temp[i] = array[i];
}
// 重新指向地址
array = temp;
}
// 不需要扩容,直接加
array[count] = value;
// 当前存的数+1
++count;
}
// 按值删
public void Remove(T value)
{
// 实现略
}
// 按位置删
public void RemoveAt(int index)
{
// 实现略
}
// 索引器
public T this[int index]
{
get
{
// 实现略
}
set
{
// 实现略
}
}
/// <summary>
/// 获取容量
/// </summary>
public int Capacity
{
get
{
return array.Length;
}
}
/// <summary>
/// 得到具体存了几个值
/// </summary>
public int Count
{
get
{
return count;
}
}
}
主函数内
// 声明泛型int的ArrayList
ArrayList<int> array = new ArrayList<int>();
Console.WriteLine(array.Count); // 0
Console.WriteLine(array.Capacity); // 16
array.Add(1);
array.Add(2);
array.Add(3);
Console.WriteLine(array.Count); // 3
Console.WriteLine(array.Capacity); // 16
for (int i = 0; i < array.Count; i++)
{
Console.WriteLine(array[i]); // 1 2 3
}
Console.WriteLine(array[1]); // 2
Console.WriteLine(array[-1]); // 索引不合法 返回默认值0
Console.WriteLine(array[3]); // 索引不合法 返回默认值0
array.RemoveAt(0); // 移除索引为0的元素
Console.WriteLine(array.Count); // 2
for (int i = 0; i < array.Count; i++)
{
Console.WriteLine(array[i]); // 2 3
}
array.Remove(3); // 移除值为3的元素
Console.WriteLine(array.Count); // 1
for (int i = 0; i < array.Count; i++)
{
Console.WriteLine(array[i]); // 2
}
Console.WriteLine(array[0]); // 2
array[0] = 99;
Console.WriteLine(array[0]); // 99
// 泛型string的ArrayList同理
ArrayList<string> array2 = new ArrayList<string>();
7.4 练习题代码
using System;
namespace Lesson6_练习题
{
//class语句块外 namespace语句块内
#region 练习题一
//用泛型实现一个单例模式基类
//泛型单例基类 泛型约束一下 必须要有公共无参构造
//缺点:有无参构造暴露给外部使用 不能私有 但其实无伤大雅 有经验的程序员不会new单例类
class SingleBase<T> where T : new()
{
private static T instance = new T();
public static T Instance
{
get
{
return instance;
}
}
}
//游戏管理类 继承泛型单例基类
class GameMgr : SingleBase<GameMgr>
{
public int value = 10;
}
//测试类1 继承泛型单例基类
class Test : SingleBase<Test>
{
public int value = 10;
}
//测试类2 不继承泛型单例基类 要自己手动写单例实现
class Test2
{
private static Test2 instance = new Test2();
public int value = 10;
private Test2()
{
}
public static Test2 Instance
{
get
{
return instance;
}
}
}
#endregion
#region 练习题二
//利用泛型知识点,仿造ArrayList实现一个不确定数组类型的类,实现增删查改方法
//我们自己写的泛型ArrayList类
//和C#自带的ArrayList相比 我们自己写的可以约束类型 不能什么类都往里面加 不用装箱拆箱
class ArrayList<T>
{
//泛型数组
private T[] array;
//当前正儿八经存了多少数
private int count;
//构造函数
public ArrayList()
{
//默认一一开始存的数为0
count = 0;
//一开始的容量就是16
array = new T[16];
}
//增
public void Add(T value)
{
//是否需要扩容
if (count >= Capacity)
{
//每次 家扩容2倍
T[] temp = new T[Capacity * 2];
//遍历搬家
for (int i = 0; i < Capacity; i++)
{
temp[i] = array[i];
}
//重新指向地址
array = temp;
}
//不需要扩容 直接加
array[count] = value;
//当前存的数+1
++count;
}
//按值删
public void Remove(T value)
{
//这个地方 不是小于数组的容量
//是小于 具体存了几个值
//给index一个默认值-1
int index = -1;
//遍历判断
for (int i = 0; i < Count; i++)
{
//不能用==去判断 因为 不是所有的类型都重载了运算符
if (array[i].Equals(value))
{
//相等赋值相等的值的索引给index 跳出循环
index = i;
break;
}
}
//只要不等于-1 就证明找到了 那就去移除
if (index != -1)
{
//删的调用按位置删的重载
RemoveAt(index);
}
}
//按位置删
public void RemoveAt(int index)
{
//判断索引合法不合法
if (index < 0 || index >= Count)
{
Console.WriteLine("索引不合法");
return;
}
//从传入的index开始 后面的元素逐个往前放
for (; index < Count - 1; index++)
{
array[index] = array[index + 1];
}
//把一个数移除了 后面的往前面放 那么最后一个 要移除
//给最后一个数一个默认值
array[Count - 1] = default(T);
//当前存的数-1
--count;
}
//索引器
public T this[int index]
{
get
{
if (index < 0 || index >= Count)
{
Console.WriteLine("索引不合法");
return default(T);
}
return array[index];
}
set
{
if (index < 0 || index >= Count)
{
Console.WriteLine("索引不合法");
return;
}
array[index] = value;
}
}
/// <summary>
/// 获取容量
/// </summary>
public int Capacity
{
get
{
return array.Length;
}
}
/// <summary>
/// 得到具体存了几个值
/// </summary>
public int Count
{
get
{
return count;
}
}
}
#endregion
class Program
{
static void Main(string[] args)
{
Console.WriteLine("泛型约束练习题");
//主函数内
#region 练习题一
GameMgr.Instance.value = 10;//直接.Instance使用单例
//GameMgr g = new GameMgr(); //缺点:有无参构造暴露给外部使用 不能私有 但其实无伤大雅 有经验的程序员不会new单例类
Test.Instance.value = 10;//直接.Instance使用单例
//Test t = new Test(); //缺点:有无参构造暴露给外部使用 不能私有 但其实无伤大雅 有经验的程序员不会new单例类
Test2.Instance.value = 10;//直接.Instance使用单例 但是是自己手动实现的 没有继承泛型单例基类
#endregion
#region 练习题二
//声明泛型int的ArrayList
ArrayList<int> array = new ArrayList<int>();
Console.WriteLine(array.Count);//0
Console.WriteLine(array.Capacity);//16
array.Add(1);
array.Add(2);
array.Add(3);
Console.WriteLine(array.Count);//3
Console.WriteLine(array.Capacity);//16
for (int i = 0; i < array.Count; i++)
{
Console.WriteLine(array[i]);//1 2 3
}
Console.WriteLine(array[1]);//2
Console.WriteLine(array[-1]);//索引不合法 返回默认值0
Console.WriteLine(array[3]);//索引不合法 返回默认值0
array.RemoveAt(0);//移除索引为0的元素
Console.WriteLine(array.Count);//2
for (int i = 0; i < array.Count; i++)
{
Console.WriteLine(array[i]);//2 3
}
array.Remove(3);//移除值为3的元素
Console.WriteLine(array.Count);//1
for (int i = 0; i < array.Count; i++)
{
Console.WriteLine(array[i]);//2
}
Console.WriteLine(array[0]);//2
array[0] = 99;
Console.WriteLine(array[0]);//9
//泛型string的ArrayList同理
ArrayList<string> array2 = new ArrayList<string>();
#endregion
}
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com