関数の引数で TimeSpan を Optional として使いたい場面があった。つまり下記のように使いたい。
Sub Test(Optional oTimeSpan As TimeSpan = New TimeSpan(0, 0, 0))
:
End Sub
でも上記のような書き方をすると・・・
「定数式が必要です。」と怒られてしまいました。
この場面で初期値としてどんな定数式が使えるか調べてみましたが、指定できそうなのは Nothing しか無さそうでした。Nothing を指定すると、とりあえずエラーになりません。
しかしこのまま実行すると、当然、エラーになるので、Nothing を処理するコードを加えてみます。そうすると・・・
「’Is’ 演算子は型 ‘TimeSpan’ のオペランドを受け付けません。オペランドは参照型または Null 許容型でなければなりません。」というエラーが出てしまいました。
TimeSpan の型は決められているので、それを参照型にすることはできません(参照渡しの引数と混同しないように)。ということで TimeSpan を「Null 許容型」にしてみましょう。
「Null 許容型」(「 Nothing 許容型」と言うべきか)にするには、下図のように、型宣言の末尾にクエスションマーク “?” を付けて As TimeSpan? と書きます。(または As Nullable(Of TimeSpan) と書いても同じ)
しかし今度は別のところで「’TotalSeconds’ は ‘TimeSpan?’ のメンバーではありません。」と言われてしまいました。「Null 許容型」にした TimeSpan のメンバを参照するには、ドット “.” ではなく、クエスションマークとドット “?.” を使う必要があります。
“oTimeSpan.TotalSeconds” を “oTimeSpan?.TotalSeconds” に書き直してみると、エラーが出なくなりました。(動作テストも問題ありませんでした)
テキストのコードも下記に載せておきます。
Private Sub Test(Optional oTimeSpan As TimeSpan? = Nothing) If oTimeSpan Is Nothing Then oTimeSpan = New TimeSpan(0, 0, 0) End If Console.WriteLine(oTimeSpan?.TotalSeconds.ToString) End Sub
以上が、関数の引数で TimeSpan を Optional として使いたい場合の回避策です。
TimeSpan だけでなく、Optional を使用すると「定数式が必要です。」と怒られる場合には使えそうなテクニックですね。
余談ですが、なにげなく下記のように書いたところ、エラーが出ずに正常に動作しているようでした。(TimeSpan 型の変数が Nothing かどうかをイコール記号 “=” で調べています)
参照型の変数の場合、Nothing かどうかを調べるには Is Nothing または IsNot Nothing を使うのが普通ですが、値型の変数の場合、”=” や “<>” を使うことができるとのことです。
ただし、この場合、実際にはその変数が「Nothing かどうか」を調べているのではなく、その変数が「初期値の状態か」を調べていることになり、混乱を引き起こす原因になり得るので、このような書き方は避けたほうが良いようです。
Visual Studio 2017 までは、参照型を「Null 許容型」にすることはできませんでしたが、C# 8.0(Visual Studio 2018?)からは参照型も「Null 許容型」にできるようになるようです。
それに伴って、参照型も値型もデフォルトでは Null を許容しなくなり、「Null 許容型」の指定をした場合に限り Null を使えるように統一されるとか。でもいきなり変更すると混乱が起きるので、当面は、過去のコードも正常に動くように、オプションとして実装されるようです。