Daniel Hilgarth が指摘したように、これを達成するための確実な方法はありません。私の提案は彼の提案に似ていますが、安全性がいくらか追加されています。プログラム全体でラッパー型を使用するコストよりもメリットの方が大きいかどうかは、自分で判断できます。
struct NonNull<T> where T : class {
private readonly T _value;
private readonly bool _isSafe;
public NonNull(T value) {
if (value == null)
throw new ArgumentNullException();
_value = value;
_isSafe = true;
}
public T Value {
get {
if (_isSafe) return _value;
throw new ArgumentNullException();
}
}
public static implicit operator T(NonNull<T> nonNull) {
return nonNull.Value;
}
}
static class NonNull {
public static NonNull<T> Create<T>(T value) where T : class {
return new NonNull<T>(value);
}
}
ラッパー型は主に意図を自己文書化するためのものであるため、ゼロで初期化された構造体でバイパスすることはほとんどありませんが、正しく初期化されたことを示すフラグを保持します。その明らかに異常なケースではArgumentNullException
、値にアクセスするときに をスローします。
class Program {
static void Main(string[] args) {
IsEmptyString(NonNull.Create("abc")); //false
IsEmptyString(NonNull.Create("")); //true
IsEmptyString(null); //won't compile
IsEmptyString(NonNull.Create<string>(null)); //ArgumentNullException
IsEmptyString(new NonNull<string>()); //bypassing, still ArgumentNullException
}
static bool IsEmptyString(NonNull<string> s) {
return StringComparer.Ordinal.Equals(s, "");
}
}
さて、これは時折 NRE よりも優れていますか? 多分。定型引数のチェックを大幅に節約できます。あなたの状況にとって価値があるかどうかを判断する必要があります。F# が提供するようなコンパイラ サポートが不足しているため、コンパイル時の null 安全性を提供する方法はありませんが、(ほぼ間違いなく) 実行時の安全性を緩和することはできます。
Microsoft のカスタマー フィードバック サイトでこの問題に賛成票を投じることをお勧めします: C# で null 非許容参照型を追加する