web-dev-qa-db-ja.com

null条件付き演算子はnull許容型で動作しませんか?

私はc#6でコードを書いていますが、奇妙な理由でこれが機能します

var value = objectThatMayBeNull?.property;

しかし、これはしません:

int value = nullableInt?.Value;

動作しないということは、Cannot resolve symbol 'Value'。 null条件演算子?.は機能していませんか?

61
linkerro

さて、私はいくつかの思考とテストを行いました。これが起こることです:

_int value = nullableInt?.Value;
_

コンパイル時にこのエラーメッセージが表示されます。

タイプ「int」には「Value」の定義が含まれていません

つまり、_?_は_int?_を実際のint値に「変換」します。これは実質的に次と同じです。

_int value = nullableInt ?? default(int);
_

結果は整数であり、明らかにValueを持ちません。

さて、これは役立つでしょうか?

_int value = nullableInt?;
_

いいえ、その構文は許可されていません。

それでは何ですか?この場合、.GetValueOrDefault()を使用し続けるだけです。

_int value = nullableInt.GetValueOrDefault();
_
31
Patrick Hofman

これは、null条件演算子を使用して値にアクセスしても意味がないためです。

  • x?.pを適用すると、pはnullを許可しない値型Tで、結果はT?型になります。同様に、nullableInt?.Value操作の結果mustはNULL可能です。
  • Nullable<T>に値がある場合、nullableInt?.Valueの結果は値自体と同じになります
  • Nullable<T>に値がない場合、結果はnullになりますが、これも値自体と同じです。

?.演算子を使用してValueにアクセスすることは意味がありませんが、null許容値タイプの他のプロパティにアクセスすることは意味があります。演算子は、null許容値型と参照型で一貫して動作するため、これら2つの実装は同じ動作を生成します。

class PointClass {
    public int X { get; }
    public int Y { get; }
    public PointClass(int x, int y) { X = x; Y = y; }
}
struct PointStruct {
    public int X { get; }
    public int Y { get; }
    public PointStruct(int x, int y) { X = x; Y = y; }
}
...
PointClass pc = ...
PointStruct? ps = ...
int? x = pc?.X;
int? y = ps?.Y;

Nullを許可するstructの場合、演算子を使用すると、基になる型PointStructのプロパティにアクセスできます。また、参照タイプPointClass

19
dasblinkenlight

Null許容型に関しては、_?._演算子は_if not null, use the wrapped value_と言っています。したがって、nullable intでは、nullableの値が_8_である場合、_?._の結果は_8_を含むnullableではなく_8_になります。 Valueintのプロパティではないため、エラーが発生します。

そのため、プロパティValueを使用しようとする例はまったく正しくありませんが、次のように動作します。

var x = nullableInt?.ToString();

ヌル合体演算子_??_を検討してください。

_var x = nullableInt ?? 0;_

ここで、オペレーターは_if null, return 0, otherwise return the value inside the nullable_と言います。この場合はintです。 _?._演算子は、nullableのコンテンツの抽出に関して同様に実行しています。

特定の例では、_??_演算子ではなく、_?._演算子と適切なデフォルトを使用する必要があります。

8
Jeff Yates

私は基本的に他の答えに同意します。観測された動作が何らかの形式の信頼できるドキュメントによってバックアップされることを望んでいました。

C#6.0仕様はどこにも見つからないので(まだ出ていますか?)、「ドキュメント」に最も近いのは 2014年2月3日のC#言語設計ノート です。そこにある情報が現在の状況をまだ反映していると仮定すると、観察された行動を正式に説明する関連部分がここにあります。

セマンティクスは、式が1回だけ評価されることを除いて、3項演算子をnullの等価性チェック、nullリテラル、および演算子の非質問マーク付きアプリケーションに適用するようなものです。

e?.m(…)   =>   ((e == null) ? null : e0.m(…))
e?.x      =>   ((e == null) ? null : e0.x)
e?.$x     =>   ((e == null) ? null : e0.$x)
e?[…]     =>   ((e == null) ? null : e0[…])

e0eと同じです。eがNULL値を許可する値型である場合を除き、その場合はe0e.Valueです。

最後のルールを以下に適用します:

nullableInt?.Value

...意味的に等価な式は次のようになります。

((nullableInt == null) ? null : nullableInt.Value.Value)

明らかに、nullableInt.Value.Valueはコンパイルできません。それはあなたが観察したことです。

その特別なルールを具体的にnullable型に適用するという設計決定がなされた理由については、dasblinkenlightの答えがそれをうまくカバーしていると思うので、ここでは繰り返しません。


さらに、仮に仮に、null許容型に対するこの特別なルールがなく、式nullableInt?.Valueが元々考えていたようにコンパイルして動作したとしても...

// let's pretend that it actually gets converted to this...
((nullableInt == null) ? null : nullableInt.Value)

それでも、質問からの次のステートメントは無効であり、コンパイルエラーが生成されます。

int value = nullableInt?.Value; // still would not compile

それでも機能しない理由は、nullableInt?.Value式のタイプがintではなくint?になるためです。したがって、value変数のタイプをint?に変更する必要があります。

これは 2014年2月3日のC#言語設計ノート でも正式に説明されています:

結果の型は、基になる演算子の右側の型Tに依存します。

  • Tが(既知である)参照型である場合、式の型はTです
  • Tがnull不可の値型であることがわかっている場合、式の型はT?
  • TがNULL値可能型であることがわかっている場合、式の型はTです。
  • それ以外の場合(つまり、Tが参照型であるか値型であるかが不明な場合)、式はコンパイル時エラーです。

ただし、コンパイルするために次の記述を強制される場合:

int? value = nullableInt?.Value;

...それはかなり無意味に思えます、そしてそれは単にすることと何の違いもありません:

int? value = nullableInt;

他の人が指摘したように、あなたのケースでは、おそらく null-conditional operator ?? ではなく、 null-coalscing operator ?. を使用するつもりでした。 _。

5
sstan

intにはValueプロパティがありません。

考慮してください:

_var value = obj?.Property
_

以下と同等です:

_value = obj == null ? null : obj.Property;
_

それはintでは意味がないため、_int?_を介した_?._では意味がありません。

ただし、古いGetValueOrDefault()は_int?_で意味をなします。

または、_?_はnullを許可するものを返さなければならないので、単純に:

_int? value = nullableInt;
_
0
Jon Hanna

単に(上記のスタンの回答に基づいて)

var value = objectThatMayBeNull?.property;

コンパイラによって評価されます

var value = (objectThatMayBeNull == null) ? null : objectThatMayBeNull.property

そして

int value = nullableInt?.Value;

お気に入り

int value = (nullableInt == null) ? null : nullableInt.Value.Value;

いつ nullableInt.Value.ValueCannot resolve symbol 'Value' 構文エラー!

0
honzakuzel1989

Null条件演算子は、null許容変数もラップ解除します。それで、「?.」の後に演算子の場合、「Value」プロパティは不要になりました。

どうやってこれに出会ったかについて、より詳細に説明する投稿を書きました。あなたが疑問に思っている場合

http://www.ninjacrab.com/2016/09/11/c-how-the-null-conditional-operator-works-with-nullable-types/

0
Min