不受限制的型別參數注釋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? 表示的實例 TIf 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 disableIf 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]TFor 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.

設計會議Design meetings