在定义泛型类型时,可以指定一个或者多个约束来限制泛型参数的范围。在实例化一个泛型类型时,若提供的类型不满足约束要求,编译器就会报错。
new 约束要求类型参数必须有公共无参数构造函数,与其它约束一起使用时,new() 约束必须放在最后。因为值类型都具有默认的公共无参构造函数,所以所有的值类型都满足 new() 约束。
class DataStore where T : new()
{
public T Data { get; set; }
}
// int 是值类型,满足约束
var store = new DataStore(); struct 约束要求类型参数只能是非空值类型,struct 类型不能和 new() 同时使用,原因前面已经说过,即所有的值类型自动满足 new() 约束。
class DataStore where T : struct
{
public T Data { get; set; }
}
// 报错,因为 string 是引用类型
var store = new DataStore();
// 报错,因为是可空值类型
var store = new DataStore();
//正确,int 是值类型
var store = new DataStore(); class 约束要求类型参数只能是引用类型,比如类、接口、委托、数组。在 C# 8.0 即以后,若打开了 nullable 开关,则必须是不可空引用类型。
class DataStore where T : class
{
public T Data { get; set; } = default!;
}
// 报错,int 不是引用类型
var store = new DataStore();
// 在C#8.0以上打开 nullable 开关后报错
// 因为是可空的
var store = new DataStore();
// 正确,string 是不可空引用类型
var store = new DataStore(); class? 约束和 class 类似,都要求类型参数必须是引用类型,但对是否是 nullable 没有要求。
class DataStore where T : class?
{
public T Data { get; set; }
}
// 正确,无论可空或者不可空的引用类型都可以
var store = new DataStore();
// 正确,无论可空或者不可空的引用类型都可以
var store = new DataStore(); BaseClass 约束要求类型参数必须是指定的基类或者派生自指定的基类。在 C# 8.0 即以后,若打开了 nullable 开关,则必须是不可空的,BaseClass? 则对可空性不做要求,既可以为不可空的,也可以为可空的。
public class Person
{
public string Name { get; set; } = string.Empty;
}
public class Student : Person
{
public int Score { get; set; }
}
class DataStore where T : Person
{
public T Data { get; set; } = default!;
}
// 错误,Person? 为可空类型
// 如需适用可空类型,使用 where T : Person? 约束
var store = new DataStore();
// 正确,Student 派生自 Person
var store = new DataStore(); 此外,还有类似的 Interface 和 Interface? 约束,即类型参数必须实现指定的接口。在 C# 8.0 及以后版本,若打开了 nullable 开关,类型参数必须不可空,Interface? 则不做要求
class DataStore where T : IComparable
{
public T Data { get; set; } = default!;
} where T : U 约束要求类型参数 T 必须是 U 或者 U的子类型。开启了 nullable 开关后,两者的可空性必须相同。
public class List
{
public void Add(List items) where U : T
} C# 8.0 引入了 notnull 约束, 要求类型参数必须非空,既可以是非空值类型,也可以是非空引用类型。
class DataStore where T : notnull
{
public T Data { get; set; } = default!;
}
// 错误,int? 为可空值类型
var store = new DataStore();
// 错误,Person? 为可空引用类型
var store = new DataStore();
// 正确
var store = new DataStore();
// 正确
var store = new DataStore(); 从 C# 7.3 开始,C# 允许使用 System.Enum 作为基类约束,用于限制类型参数为枚举类型。
public static IEnumerable<(string Label, int Value)> GetEnumChoices() where T : System.Enum
{
var values = Enum.GetValues(typeof(T));
var list = new List<(string Label, int Value)>();
foreach (int value in values)
list.Add((Enum.GetName(typeof(T), value)!, value));
return list;
} 以上代码定义了一个从枚举获取值-名称对通用方法,Enum 约束让代码可以安全调用 Enum.GetValues 方法,无需额外的代码来判断类型是不是枚举。
委托约束和枚举约束一样,也是一种特殊类型的基类约束,用于限定类型参数必须为委托。
public static TDelegate? TypeSafeCombine(this TDelegate source, TDelegate target)
where TDelegate : System.Delegate
=> Delegate.Combine(source, target) as TDelegate; 通过使用 System.Delegate 约束,上述代码可以安全地调用 Delegate.Combine 来合并两个委托。否则就需要额外的代码来判断类型是不是委托。
| 留言与评论(共有 0 条评论) “” |