web-dev-qa-db-ja.com

ヌル条件演算子はブール値ではなくブール値と評価しますか?予想通り

VS 2010から2015にアップグレードしました。新しい null-conditional operator が好きです。これはnull-propagationとも呼ばれます。これにより、コードを簡素化できます。次に例を示します。

string firstCustomerName = customers?[0].Name; // null if customers or the first customer is null

もう1つ:

int? count = customers?[0]?.Orders?.Count();  // null if customers, the first customer, or Orders is null

これは、Nullable<int>intを返し、有効なカウントとそれ以前のnullsを区別する場合でも、Enumerable.Countを返します。これは非常に直感的で非常に便利です。

しかし、なぜこれが期待どおりにコンパイルおよび動作するのですか(falseを返します):

string text = null;
bool contains = text?.IndexOf("Foo", StringComparison.CurrentCultureIgnoreCase) >= 0;

bool?(返されません)を返すか、コンパイルしない必要があります。

23
Tim Schmelter

あなたが実際に持っているのは

string text = null;
int? index = text?.IndexOf("Foo", StringComparison.CurrentCultureIgnoreCase);
bool contains = index >= 0;

int? >= intは完全に合法です。

分割された理由は 演算子のドキュメント 状態 "条件付きメンバーアクセスとインデックス操作のチェーン内の1つの操作がnullを返す場合、チェーンの残りの部分は実行は停止します。式の優先順位が低い他の操作は続行されます。 "つまり、.?は、「値を作成」する前に、同じ優先順位以上のもののみを評価します。

演算子の優先順位 を見ると、「関係演算子と型テスト演算子」がリストのはるかに低いため、>=が適用される前に値が作成されます。


UPDATE:コメントで取り上げられたので、ここに>=と他の演算子が処理するときの動作に関するC#5仕様のセクションがありますnull許容値を使用します。 C#6のドキュメントが見つかりませんでした。

7.3.7リフトされたオペレーター

リフト演算子を使用すると、null許容でない値型を操作する事前定義およびユーザー定義の演算子を、これらの型のnull許容形式でも使用できます。リフト演算子は、以下で説明するように、特定の要件を満たす事前定義およびユーザー定義の演算子から構成されます。

  • 単項演算子の場合
    + ++ - -- ! ~

    オペランドと結果の型が両方ともNULL可能でない値型である場合、リフトされた形式の演算子が存在します。持ち上げられたフォームは、単一の?オペランドと結果タイプの修飾子。オペランドがNULLの場合、リフトされた演算子はNULL値を生成します。それ以外の場合、リフトされた演算子はオペランドをアンラップし、基になる演算子を適用して、結果をラップします。

  • 二項演算子の場合
    + - * / % & | ^ << >>

    オペランドと結果の型がすべてnull許容でない値型である場合、リフトされた形式の演算子が存在します。持ち上げられたフォームは、単一の?各オペランドと結果タイプの修飾子。リフトされた演算子は、一方または両方のオペランドがnullの場合、null値を生成します(§7.11.3で説明されているように、bool?タイプの&および|演算子は例外です)。それ以外の場合、リフトされた演算子はオペランドをアンラップし、基になる演算子を適用して、結果をラップします。

  • 等式演算子の場合
    == !=

    オペランドタイプが両方ともnull許容でない値タイプであり、結果タイプがブール値である場合、演算子のリフト形式が存在します。持ち上げられたフォームは、単一の?各オペランドタイプの修飾子。解除された演算子は、2つのnull値が等しいと見なし、null値がnull以外の値と等しくないと見なします。両方のオペランドがnull以外の場合、持ち上げられた演算子はオペランドをアンラップし、基になる演算子を適用してブール結果を生成します。

  • 関係演算子の場合
    < > <= >=

    オペランドタイプが両方ともnull許容でない値タイプであり、結果タイプがブール値である場合、演算子のリフト形式が存在します。持ち上げられたフォームは、単一の?各オペランドタイプの修飾子。リフトされた演算子は、一方または両方のオペランドがnullの場合、値falseを生成します。それ以外の場合、持ち上げられた演算子はオペランドをアンラップし、基になる演算子を適用してブール結果を生成します。

29

このコードを使用し、xにカーソルを合わせると、xが_int?_であることがわかります。

_var x = text?.IndexOf("Foo", StringComparison.CurrentCultureIgnoreCase);
bool contains = x >= 0;
_

したがって、入力はまだ正しいです。

次に、_x >= 0_を調べます。これは_int? >= int_です。どうやら、null許容構造体と非null許容構造体の間に演算子があります。それが機能する理由です。

ILを見ると、実際にはHasValueGetValueOrDefault()を呼び出していることがわかります。これを実行しているオペレーターがいると思いますが、参照ソースで見つからなかったため、CLRまたはコンパイラーにあるはずです。

_instance !0 valuetype [mscorlib]System.Nullable`1<int32>::GetValueOrDefault()

...
instance bool valuetype [mscorlib]System.Nullable`1<int32>::get_HasValue()
_
3
Patrick Hofman