C#——泛型类型约束关键字new和where
new 约束
new 约束指定泛型类声明中的类型实参必须有公共的无参数构造函数。 若要使用 new 约束,则该类型不能为抽象类型。
当泛型类创建类型的新实例时,请将 new 约束应用于类型参数,如下面的示例所示:
class ItemFactory<T> where T : new()
{
public T GetNewItem()
{
return new T();
}
}
当与其他约束一起使用时,new() 约束必须最后指定:
public class ItemFactory2<T>
where T : IComparable, new()
{ }
where(泛型类型约束)
泛型定义中的 where 子句指定对用作泛型类型、方法、委托或本地函数中类型参数的参数类型的约束。 约束可指定接口、基类或要求泛型类型为引用、值或非托管类型。 它们声明类型参数必须具备的功能。
例如,可以声明一个泛型类 MyGenericClass,以使类型参数 T 实现 IComparable 接口:
public class AGenericClass<T> where T : IComparable<T> { }
where 子句还可包括基类约束。 基类约束表明用作该泛型类型的类型参数的类型具有指定的类作为基类(或者是该基类)。 该基类约束一经使用,就必须出现在该类型参数的所有其他约束之前。 某些类型不允许作为基类约束:Object、Array 和 ValueType。 在 C# 7.3 之前,Enum、Delegate 和 MulticastDelegate 也不允许作为基类约束。 以下示例显示现可指定为基类的类型:
public class UsingEnum<T> where T : System.Enum { }
public class UsingDelegate<T> where T : System.Delegate { }
public class Multicaster<T> where T : System.MulticastDelegate { }
在 C# 8.0 及更高版本中的可为 null 上下文中,强制执行基类类型的为 null 性。 如果基类不可为 null(例如 Base),则类型参数必须不可为 null。 如果基类可为 null(例如 Base?),则类型参数可以是可为 null 或不可为 null 的引用类型。 当基类不可为 null 时,如果类型参数是可为 null 的引用类型,编译器将发出警告。
where 子句可指定类型为 class 或 struct。 struct 约束不再需要指定 System.ValueType 的基类约束。 System.ValueType 类型可能不用作基类约束。 以下示例显示 class 和 struct 约束:
class MyClass<T, U>
where T : class
where U : struct
{ }
在 C# 8.0 及更高版本中的可为 null 上下文中,class 约束要求类型是不可为 null 的引用类型。 若要允许可为 null 的引用类型,请使用 class? 约束,该约束允许可为 null 和不可为 null 的引用类型。
where 子句可能包含 notnull 约束。 notnull 约束将类型参数限制为不可为 null 的类型。 该类型可以是值类型,也可以是不可为 null 的引用类型。 对于在 nullable enable 上下文中编译的代码,从 C# 8.0 开始可以使用 notnull 约束。 与其他约束不同,如果类型参数违反 notnull 约束,编译器会生成警告而不是错误。 警告仅在 nullable enable 上下文中生成。
包含 notnull 约束的泛型声明可以在可为 null 的不明显上下文中使用,但编译器不会强制执行约束。
#nullable enable
class NotNullContainer<T>
where T : notnull
{
}
#nullable restore
where 子句还可包括 unmanaged 约束。 unmanaged 约束将类型参数限制为名为“非托管类型”的类型。 unmanaged 约束使得在 C# 中编写低级别的互操作代码变得更容易。 此约束支持跨所有非托管类型的可重用例程。 unmanaged 约束不能与 class 或 struct 约束结合使用。 unmanaged 约束强制该类型必须为 struct:
class UnManagedWrapper<T>
where T : unmanaged
{ }
where 子句也可能包括构造函数约束 new()。 该约束使得能够使用 new 运算符创建类型参数的实例。 new() 约束可以让编译器知道:提供的任何类型参数都必须具有可访问的无参数构造函数。 例如:
对于多个类型参数,每个类型参数都使用一个 where 子句,例如:
public interface IMyInterface { }
namespace CodeExample
{
class Dictionary<TKey, TVal>
where TKey : IComparable<TKey>
where TVal : IMyInterface
{
public void Add(TKey key, TVal val) { }
}
}
还可将约束附加到泛型方法的类型参数,如以下示例所示:
public void MyMethod<T>(T t) where T : IMyInterface { }
请注意,对于委托和方法两者来说,描述类型参数约束的语法是一样的:
delegate T MyDelegate<T>() where T : new();