null許容型をまねて、null不可能型とかを妄想してみた

null許容型は、値型なのにnullを格納できる型で、C#ではこう書く。

int? i = null;  // nullが格納できる!

これと同じ考え方で、参照型なのにnullを格納できない型とかあると便利じゃね?と言うことで、どんな感じになるのか以下妄想。


null許容型は値型の末尾に?を付けることを真似て、null不可能型では参照型の末尾に!を付ける。

// stringのnull不可能型には文字列リテラルかstringのnull不可能型が初期化/代入可能
string! str = "";
string! str2 = str;

// null不可能型にnullは初期化/代入不可
// str = null;

// strに通常のstringは初期化/代入不可
string str3 = "";
// str = str3;

// ??演算子の最右項に文字リテラルかnull不可能型を置けばコンパイル可能
str = str3 ?? "";
// null不可能型は??演算子の最右項以外には置けない
// str = str2 ?? "";

// null不可能型を通常の型に初期化/代入可能
str3 = str2;


null指定型というのも妄想してみた。
null指定型の変数にnullを初期化/代入すると、nullの代わりに指定した値が代入される。

// クラスレベルで指定する(sealedの必要あり)
public sealed class Hoge ifnullthen Hoge.None
{
    // static readonlyのnull不可能型のみ指定可能
    // public classはpublic、internal classはinternal以上の可視性が必要
    public static readonly Hoge! None = new Hoge();
}

クラスレベルでの指定は制限が大きいが、より制約の小さい変数レベルの指定も可能。

public class Hoge
{
    internal static readonly Hoge! None = new Hoge();
    internal static readonly Hoge! None2 = new Hoge();
}

// :の後にnullの代わりに使用する値を指定する
// その変数を使用するスコープで見えるnull不可能型が指定可能
Hoge:Hoge.None h1 = null;

// クラスが同じならHoge.は省略可能
Hoge:None2 h2 = h1; // この場合どーすれば・・・
                    // setterでフックするならHoge.Noneだろうし、
                    // getterでフックするならHoge.None2になる
                    // setterでフックしたほうが効率はいいだろうけど、
                    // getterでフックしたほうが自然かな


うーん、もっと煮詰めれば結構面白いかも。