C# 中使用泛型约束来限制类型参数范围

定义泛型类型时,可以指定一个或者多个约束来限制泛型参数的范围。在实例化一个泛型类型时,若提供的类型不满足约束要求,编译器就会报错。

new() 约束

new 约束要求类型参数必须有公共无参数构造函数,与其它约束一起使用时,new() 约束必须放在最后。因为值类型都具有默认的公共无参构造函数,所以所有的值类型都满足 new() 约束。

class DataStore where T : new() { public T Data { get; set; } } // int 是值类型,满足约束 var store = new DataStore();

struct 约束

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 和 class?约束

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 }

notnull约束

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 GetEnumChoices() where T : System.Enum { var values = Enum.GetValues(typeof(T)); var list = new List(); 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 来合并两个委托。否则就需要额外的代码来判断类型是不是委托。

郑重声明:本文内容及图片均整理自互联网,不代表本站立场,版权归原作者所有,如有侵权请联系管理员(admin#wlmqw.com)删除。
上一篇 2022年7月1日 15:38
下一篇 2022年7月1日 15:38

相关推荐

联系我们

联系邮箱:admin#wlmqw.com
工作时间:周一至周五,10:30-18:30,节假日休息