手記
この記事は機能仕様です。 仕様は、機能の設計ドキュメントとして機能します。 これには、提案された仕様の変更と、機能の設計と開発時に必要な情報が含まれます。 これらの記事は、提案された仕様の変更が最終決定され、現在の 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? はその参照型の null 許容インスタンスを表します。
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? は、Nullable<T> が値型である場合、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 制約を使用するとエラーになります。
デザイン会議
C# feature specifications