共用方式為


不受限制的類型參數批注

注意

本文是功能規格。 規格可作為功能的設計檔。 其中包含建議的規格變更,以及功能設計和開發期間所需的資訊。 這些文章會發佈,直到提議的規格變更完成並併併入目前的ECMA規格為止。

功能規格與已完成實作之間可能有一些差異。 這些差異是在 的相關語言設計會議(LDM)注意事項中擷取的。

規格的文章中,您可以深入了解將功能規格納入 C# 語言標準的過程

冠軍問題:https://github.com/dotnet/csharplang/issues/3297

總結

針對不受實值型別或參考型別限制的類型參數,允許可為 Null 的註釋:T?

static T? FirstOrDefault<T>(this IEnumerable<T> collection) { ... }

? 批注

在 C#8 中,? 批注只能套用至明確限制為實值型別或參考型別的類型參數。 在 C#9 中,不論條件約束為何,? 批注都可以套用至任何類型參數。

除非類型參數明確限制為實值型別,否則批注只能在 #nullable enable 內容中套用。

如果型別參數 T 取代為參考型別,則 T? 代表該參考型別的可為空實例。

var s1 = new string[0].FirstOrDefault();  // string? s1
var s2 = new string?[0].FirstOrDefault(); // string? s2

如果 T 取代為實值型別,則 T? 代表 T的實例。

var i1 = new int[0].FirstOrDefault();  // int i1
var i2 = new int?[0].FirstOrDefault(); // int? i2

如果 T 取代為批注型別 U?,則 T? 代表批注型別 U?,而不是 U??

var u1 = new U[0].FirstOrDefault();  // U? u1
var u2 = new U?[0].FirstOrDefault(); // U? u2

如果 T 取代為類型 U,則 T? 代表 U?,即使在 #nullable disable 內容中也一樣。

#nullable disable
var u3 = new U[0].FirstOrDefault();  // U? u3

針對傳回值,T? 相當於 [MaybeNull]T;對於自變數值,T? 相當於 [AllowNull]T。 從使用 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 條件約束

為了與現有程序代碼相容,其中覆寫和明確實作的泛型方法無法包含明確約束條件,覆寫或明確實作的方法中,當 T 是值型別時,T? 會被視為 Nullable<T>

若要允許對受限於參考類型的類型參數進行註釋,C#8 允許在被覆寫或明確實作的方法上使用明確的 where T : classwhere T : struct 約束條件。

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 條件約束。

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 條件約束是錯誤的。 當覆寫或介面方法中的對應型別參數受限於參考型別或實值型別時,使用 default 條件約束是錯誤的。

設計會議