不受约束的类型参数批注Unconstrained type parameter annotations
总结Summary
允许不受值类型或引用类型约束的类型参数的可以为 null 的批注: T?
。Allow nullable annotations for type parameters that are not constrained to value types or reference types: T?
.
static T? FirstOrDefault<T>(this IEnumerable<T> collection) { ... }
?
注释?
annotation
在 c # 8 中, ?
批注仅适用于显式约束为值类型或引用类型的类型参数。In C#8, ?
annotations could only be applied to type parameters that were explicitly constrained to value types or reference types.
在 c # 9 中, ?
批注可应用于任何类型参数,而不考虑约束。In C#9, ?
annotations can be applied to any type parameter, regardless of constraints.
除非将类型形参显式约束为值类型,否则只能在上下文中应用注释 #nullable enable
。Unless a type parameter is explicitly constrained to value types, annotations can only be applied within a #nullable enable
context.
如果类型参数 T
替换为引用类型,则 T?
表示该引用类型的可以为 null 的实例。If a type parameter T
is substituted with a reference type, then T?
represents a nullable instance of that reference type.
var s1 = new string[0].FirstOrDefault(); // string? s1
var s2 = new string?[0].FirstOrDefault(); // string? s2
如果 T
用值类型替换,则 T?
表示的实例 T
。If T
is substituted with a value type, then T?
represents an instance of T
.
var i1 = new int[0].FirstOrDefault(); // int i1
var i2 = new int?[0].FirstOrDefault(); // int? i2
如果 T
使用批注类型替换 U?
,则 T?
表示批注的类型 U?
而不是 U??
。If T
is substituted with an annotated type U?
, then T?
represents the annotated type U?
rather than U??
.
var u1 = new U[0].FirstOrDefault(); // U? u1
var u2 = new U?[0].FirstOrDefault(); // U? u2
如果 T
将替换为类型 U
,则 T?
表示 U?
,即使在上下文中也是如此 #nullable disable
。If T
is substituted with a type U
, then T?
represents U?
, even within a #nullable disable
context.
#nullable disable
var u3 = new U[0].FirstOrDefault(); // U? u3
对于返回值, T?
等效于 [MaybeNull]T
; 对于参数值, T?
等效于 [AllowNull]T
。For return values, T?
is equivalent to [MaybeNull]T
; for argument values, T?
is equivalent to [AllowNull]T
.
在使用 c # 8 编译的程序集中重写或实现接口时,等效性非常重要。The equivalence is important when overriding or implementing interfaces from an assembly compiled with C#8.
public abstract class A
{
[return: MaybeNull] public abstract T F1<T>();
public abstract void F2<T>([AllowNull] T t);
}
public class B : A
{
public override T? F1<T>() where T : default { ... } // matches A.F1<T>()
public override void F2<T>(T? t) where T : default { ... } // matches A.F2<T>()
}
default
约束default
constraint
为了与重写的和显式实现的泛型方法的现有代码兼容, T?
将被重写或显式实现的方法视为, Nullable<T>
其中 T
是值类型。For compatibility with existing code where overridden and explicitly implemented generic methods could not include explicit constraint clauses, T?
in an overridden or explicitly implemented method is treated as Nullable<T>
where T
is a value type.
若要允许将类型参数批注用于引用类型,c # 8 允许 where T : class
where T : struct
对已重写或显式实现的方法使用显式和约束。To allow annotations for type parameters constrained to reference types, C#8 allowed explicit where T : class
and where T : struct
constraints on the overridden or explicitly implemented method.
class A1
{
public virtual void F1<T>(T? t) where T : struct { }
public virtual void F1<T>(T? t) where T : class { }
}
class B1 : A1
{
public override void F1<T>(T? t) /*where T : struct*/ { }
public override void F1<T>(T? t) where T : class { }
}
若要允许不受引用类型或值类型约束的类型参数的批注,c # 9 允许新的 where T : default
约束。To allow annotations for type parameters that are not constrained to reference types or value types, C#9 allows a new where T : default
constraint.
class A2
{
public virtual void F2<T>(T? t) where T : struct { }
public virtual void F2<T>(T? t) { }
}
class B2 : A2
{
public override void F2<T>(T? t) /*where T : struct*/ { }
public override void F2<T>(T? t) where T : default { }
}
如果对 default
方法重写或显式实现使用以外的约束,则是错误的。It is an error to use a default
constraint other than on a method override or explicit implementation.
default
如果重写或接口方法中的相应类型参数被约束为引用类型或值类型,则使用约束是错误的。It is an error to use a default
constraint when the corresponding type parameter in the overridden or interface method is constrained to a reference type or value type.