1.泛型约束的使用
泛型约束的作用
在一个泛型方法或者说泛型接口中,传入的泛型是不确定的,但是在方法体中,我们传入一个实体,要在代码块中获取实体的各种属性如:姓名、性别等,但是问题来了,由于我们的类型是在调用的时候确定的,因此在写泛型方法或接口代码的时候我们是不确定入参类型,所以就需要一个东西来确定入参有那些属性,由此 泛型约束就冒出来了,当然 为了更好的理解 下面有代码解释—以泛型方法为例:
首先我们定义一个动物类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StudyComm.ClassFolder
{
/// <summary>
/// 动物
/// </summary>
public class AnimalClass
{
/// <summary>
/// 动物种类
/// </summary>
public string AnimalType { get; set; }
}
}
然后再定义一个鸟类 和狗类 继承自动物
public class BirdClass : AnimalClass
{
/// <summary>
/// 鸟类名字
/// </summary>
public int BirdName { get; set; }
}
public class DogClass : AnimalClass
{
/// <summary>
/// 狗的种类
/// </summary>
public string DogType { get; set; }
}
我们再定义一个人类
/// <summary>
/// 人类
/// </summary>
public class HumanClass
{
/// <summary>
/// 名字
/// </summary>
public string Name { get; set; }
/// <summary>
/// 国籍
/// </summary>
public string Nationality { get; set; }
}
再来一个中国人和日本人
/// <summary>
/// 定义一个中国人的类,并继承人类
/// </summary>
public class ChineseClass : HumanClass
{
/// <summary>
/// 身份证
/// </summary>
public string IDCard { get; set; }
}
/// <summary>
/// 定义一个日本人
/// </summary>
public class JapaneseClass : HumanClass
{
//这里面我就不加属性 因为日本人是直接继承人类 所以也具有名字和种类
}
在写好了类之后 ,我来写几个方法 在每个方法中 使用泛型约束 规定在调用的时候传入的参数必须为指定类及其子类
public class FunClass
{
/// <summary>
/// 用于展示中国人的名字
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="Type"></param>
public static void ShowHuman<T>(T Type) where T : HumanClass//泛型约束为 HumanClass类,表示只能传入人类 以及其子类
{
Console.WriteLine($"这个人是一个{Type.Name},他的国籍是{Type.Nationality}");
}
public static void ShowBirdType<T>(T Type) where T : AnimalClass//泛型约束为 AnimalClass类,表示只能传入动物类 以及其子类
{
Console.WriteLine($"这个动物的种类是{Type.AnimalType}");
}
}
我们在方法里实例化两个人 一个中国人,一个日本人
//实例化一个中国人,日本人
ChineseClass chinese = new ChineseClass() { Name="小明",Nationality="中国"};//中国人
JapaneseClass human = new JapaneseClass() { Name = "小岛秀夫", Nationality = "日本" };//日本人
上面实例化的两个人都是继承自HumanClass类 所以在调用的时候
//我们在调用的时候<>中可以指定类型为HumanClass
FunClass.ShowHuman<HumanClass>(chinese);//传入中国人
FunClass.ShowHuman<HumanClass>(human);//传入日本人
//当然 我们也可以把具体的类指定进去
FunClass.ShowHuman<ChineseClass>(chinese);//传入中国人
FunClass.ShowHuman<JapaneseClass>(human);//传入日本人
//同时 我们也可以简写为
FunClass.ShowHuman(chinese);//传入中国人
FunClass.ShowHuman(human);//传入日本人
- 以上三种写法都是可以的
- 但是 如果我们实例化个鸟和狗之后,把鸟 或者狗传入ShowHuman方法 那么就会报错
- 由于我们ShowHuman方法约束了传入的只能是HumanClass及其子类,传入的dog 是无法转化为Human等类型,故此报错
-
泛型约束的可作用场景
- 上面只是泛型方法的约束使用,但是泛型可用的地方总共有六种
名称 | 描述 |
new() | 类型参数必须具有无参数的公共构造函数。当与其他约束一起使用时,new() 约束必须最后指定。 |
接口 | 类型参数必须是指定的接口或实现指定的接口。可以指定多个接口约束。约束接口也可以是泛型的。 |
基类 | 类型参数必须是指定的基类或派生自指定的基类。 |
U | 为 T 提供的类型参数必须是为 U 提供的参数或派生自为 U 提供的参数。这称为裸类型约束。 |
结构 | 类型参数必须是值类型。可以指定除 Nullable 以外的任何值类型 |
类 | 类型参数必须是引用类型,包括任何类、接口、委托或数组类型 |
例子:
namespace StudyComm.Generic
{
class GenericConstraint<T> where T : GenericInterFace<T>//接口约束GenericInterFace为一个泛型接口
{
}
class ClassOrStructureConstraint<T, A>//类或结构约束
where T : HumanClass
where A : AnimalClass
{
}
public class ConstructorConstraint<T> where T: IComparable, new()//构造函数约束
{
//裸类型约束,当具有自己的类型参数的成员函数需要将该参数约束为包含类型的类型参数时可使用
void UConstraint<U>(List<U> list) where U : T
{ }
}
}