不受限制的類型參數批注
注意
本文是功能規格。 規格可作為功能的設計檔。 其中包含建議的規格變更,以及功能設計和開發期間所需的資訊。 這些文章會發佈,直到提議的規格變更完成並併併入目前的ECMA規格為止。
功能規格與已完成實作之間可能有一些差異。 這些差異是在 的相關語言設計會議(LDM)注意事項中擷取的。
冠軍問題: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 : class
和 where 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
條件約束是錯誤的。