C#のプロパティ結合演算子


9

C#のnull結合演算子を使用すると、コードを短縮できます

  if (_mywidget == null)
     return new Widget();
  else
     return _mywidget;

至るまで:

  return _mywidget ?? new Widget();

C#で使用したい便利な演算子は、オブジェクトのプロパティ、またはオブジェクトがnullの場合は他の値を返すことができる演算子であることに気づきました。交換したいので

  if (_mywidget == null)
     return 5;
  else
     return _mywidget.Length;

と:

  return _mywidget.Length ??! 5;

この演算子が存在しないのには何らかの理由があるに違いないと私は思わずにはいられません。コード臭?これを書くためのより良い方法はありますか?(私はnullオブジェクトパターンを知っていますが、これらの4行のコードを置き換えるためにそれを使用するのはやり過ぎのようです。)


1
ここでは条件演算子で十分でしょうか?
アノン。

1
誰かが次のようなことを可能にする何かを書いています:string location = employee.office.address.location ?? "わからない"; 。オブジェクトの1つ(employeeofficeaddressまたはlocation)がnullの場合、このwilは場所「不明」に設定します。残念ながら、誰が書いたか、どこに投稿したか覚えていません。また見つけたらこちらに投稿します!
Kristof Claes、2011年

1
この質問は、StackOverflowの方がはるかに魅力的です。
ジョブ

2
??!C ++の演算子です。:-)
James McNellis、2011年

回答:


5

それぞれのnullをチェックする必要なく、x.Prop.SomeOtherProp.ThirdPropを安全に実行できるC#言語機能が必要です。これらの種類の「深いプロパティトラバーサル」を頻繁に実行する必要があった1つのコードベースで、NaviReferenceと呼ばれる拡張メソッドを記述して、NullReferenceExceptionsを飲み込み、代わりにデフォルト値を返しました。だから私はこのようなことをすることができました:

var propVal = myThing.Navigate(x => x.PropOne.PropTwo.PropThree.PropFour, defaultValue);

これの欠点は、コードの匂いが異なることです:飲み込まれた例外です。この「正しい」ようなことをしたい場合は、lambbaを式として受け取り、その場で式を変更して、各プロパティアクセサーの周りにnullチェックを追加できます。私は「迅速かつ汚い」ために行き、これを「より良い」方法で実装しませんでした。しかし、多分私がいくつかの予備の思考サイクルを持っているとき、私はこれを再訪して、どこかにそれを掲示するでしょう。

あなたの質問にもっと直接答えるために、これが機能ではない理由は、機能を実装するコストが機能を持っていることの利点を超えているためだと思います。C#チームはどの機能に焦点を当てるかを選択する必要がありますが、これはまだトップに浮上していません。私はインサイダー情報を持っていませんが、推測です。


1
正確には私が思ったのですが、安全な方法のパフォーマンスはかなり悪いと思います(Expression.Compile()が多いため)...残念ながら、C#には実装されていません(Obj.?PropA.?Prop1のようなもの)
Guillaume86

x.PropOne.PropTwo.PropThree.PropFourは、デメテルの法則に違反するため、設計上の選択としては不適切です。メソッド/クラスを再設計して、これを使用する必要がないようにします。
ジャスティンシールド

デメテルの法則に照らして深い財産へのアクセスを不注意に避けることは、データベースを無意識に正規化するのと同じくらい危険です。あなたは行き​​過ぎることができます。
Mir

3
C#6.0では、これはNull-Conditionalオペレーター(別名Elvisオペレーター)msdn.microsoft.com/en-us/magazine/dn802602.aspx
victorvartan

15

これで、C#6でこれを簡単に実行できるようになると思います。

return _mywidget?.Length ?? 5;

ヌル条件演算子注?.左側を。_mywidget?.Lengthがnullの場合_mywidgetはnullを返します。

他の人が示唆しているように、単純な三項演算子の方が読みやすいかもしれません。


9

彼らがなぜそれをしなかったのかについては、それは本当に推測にすぎません。結局、私は信じていません?? 最初のC#バージョンでした。

とにかく、私は条件演算子を使用してそれを1日と呼びます。

return (_mywidget != null) ? _mywidget.Length : 5;

?? 条件付き演算子へのショートカットにすぎません。


5
個人的には、これは、最初にこれを行うオペレーターがいるのと同じくらい悪い(imho)です。オブジェクトから何かが欲しい...そのオブジェクトはそこにないかもしれない...それがそこにない5場合の答えとして提供しましょう。特別な演算子を要求する前に、設計を確認する必要があります。
Mooジュース

1
@ Moo-Juice私はこのコードを管理している人を想像しているだけです。うーん。何?!'
msarchet 2011年


3

個人的には、読みやすさ次第だと思います。最初の例では??、「Are you there ?? Nope、let's do this」のように非常にうまく翻訳されます。

2番目の例で??!は、明らかにWTFであり、プログラマーには何も意味しません。...オブジェクトが存在しないため、プロパティにアクセスできないため、5を返します。それはどういう意味ですか?5って何?5が良い値であるという結論にどうやって到達しますか?

要するに、最初の例は理にかなっています...そこにではなく、新しいものです。第二に、それは議論の余地があります。


2
まあ、あなたはそれを無視する必要があります??!存在しません。想像してみてください。それが一般的であり、それが使用された場合、それに基づいて決定を下します。
whatsisname '26年

3
これは、C ++のいわゆる「WTF演算子」を思い出させます(これは実際には機能します(foo() != ERROR)??!??! cerr << "Error occurred" << endl;。)
TamásSzelei

@whatsisname、それは行われている決定に適切であったという事実をより反映したものでした。そこになかったのでオブジェクトを作成することは理にかなっています。何かのプロパティを取得できないために読者に何も意味しない任意の値を割り当てることは、実際にはWTFシナリオです。
Mooジュース

私の例に巻き込まれていると思います。たぶん、0は5よりも良いでしょう。たぶん、プロパティはオブジェクトなので、_mywidgetがnullの場合、新しいfoodle()を返します。私は完全にそう思わない?? ??!より読みやすい しかし:)
ベン・フルトン

@ベン、私はオペレーター自体に焦点を当てることはあなたの質問にあまり役立たないことに同意しますが、プログラマーの認識とその恣意的なものとの類似点を描きました5。私は本当にこのようなことをしなければならない問題がもっとあると思いますが、最初の例では、特にキャッシュマネージャーなどの必要性は非常に明白です。
Moo-Juice、

2

私は人々があなたが戻ってきている「5」に追いつきすぎていると思います...おそらく0はもっと良かったでしょう:)

とにかく、私はこの問題は、ということだと思う??!実際には、スタンドアロンの演算子ではありません。これが何を意味するかを考えてみましょう:

var s = myString ??! "";

その場合、それは意味がありません。左側のオペランドがプロパティアクセサーである場合にのみ意味があります。またはこれはどうですか:

var s = Foo(myWidget.Length) ??! 0;

そこでのプロパティアクセサですが、私はまだそれが理にかなっているとは思わない(場合myWidgetnull、平均我々は呼んでいないというんFoo()すべてで?)、またはこれは単にエラーですか?

問題は、言語に自然に収まら??ないことです。


1

誰かがこれを行うユーティリティクラスを作成しました。しかし、私はそれを見つけることができません。私が見つけたのは、MSDNフォーラムで似たようなものでした(2番目の答えを見てください)。

少しの作業で、それを拡張して、サンプルでサポートされていないメソッド呼び出しやその他の式を評価できます。デフォルト値を受け入れるように拡張することもできます。


1

あなたがどこから来たのか理解しました。あなたが提供した例で誰もがアクセルを包んでいるようです。マジックナンバーは悪い考えだと私は同意しますが、特定のプロパティについては、nullの合体演算子と同等の用途があります。

たとえば、マップにバインドされたオブジェクトがあり、それらを適切な標高に配置したいとします。DTEDマップのデータには穴がある可能性があるため、-100から〜8900メートルの範囲の値だけでなく、null値を持つことも可能です。あなたは次の線に沿って何かを望むかもしれません:

mapObject.Altitude = mapObject.Coordinates.DtedAltitude ?? DEFAULT_ALTITUDE;

この場合、nullの合体演算子は、座標がまだ設定されていない場合、またはCoordinatesオブジェクトがその場所のDTEDデータを読み込めなかった場合に、デフォルトの高度を入力します。

これは非常に価値があると思いますが、なぜそれが行われなかったのかについての私の推測は、コンパイラーの複雑さに限定されています。動作が予測できない場合があります。


1

深くネストされたものを処理しない場合は、これを使用しました(C#6で導入されたnull結合演算子の前)。私にはそれはかなり明確ですが、多分それは私がそれに慣れているためかもしれません、他の人々はそれを混乱させるかもしれません。

return ( _mywidget ?? new MyWidget() {length = defaultLength}).Length;

もちろん、これは常に適用できるとは限りません。アクセスする必要のあるプロパティを直接設定できない場合や、オブジェクト自体の構築にコストがかかる場合などがあるためです。

もう1つの方法は、NullObjectパターンを使用することです。適切なデフォルト値でクラスの単一の静的インスタンスを定義し、代わりにそれを使用します。

return ( _mywidget ?? myWidget.NullInstance).Length;

0

通常、マジックナンバーは悪臭です。5が任意の数である場合、より明確に文書化されるように、それを綴るのが良いでしょう。私の意見の例外は、特定のコンテキストでデフォルト値として機能する値のコレクションがある場合です。

オブジェクトのデフォルト値のセットがある場合、それをサブクラス化してデフォルトのシングルトンインスタンスを作成できます。

次のようなもの:return(myobj ?? default_obj).Length


0

長さプロパティは通常値型であるため、nullにすることはできません。そのため、null結合演算子はここでは意味がありません。

しかし、たとえば、参照型であるプロパティを使用してそれを行うことができます。 var window = App.Current.MainWindow ?? new Window();


この例は、Currentがnullの場合に状況で何が起こるかを検討しようとしています。あなたの例は単にクラッシュします...しかし、誤った方向のチェーンのどこにでも合体することができる演算子を持つことは便利です。
ミール

-1

以前に似たようなアイデアを持つ人を見たことがありますが、ほとんどの場合、次のような新しい値をnullフィールドにも割り当てたいと思います。

return _obj ?? (_obj = new Class());

したがって、??との=割り当てを次のように組み合わせることがアイデアでした。

return _obj ??= new Class();

これは感嘆符を使用するよりも理にかなっていると思います。

このアイデアは私のものではありませんが、私はそれが本当に好きです:)


1
あなたは悪い例の犠牲者ですか?あなたの例return _obj ?? new Class();??=ここの必要がないように短くすることができます。
Matt Ellen

@マットエレン、あなたはそれを間違えました。割り当ては意図されています。私は別の選択肢を提案しています。これは、OPが要求するものとまったく同じではありませんが、同じくらい役立つと思います。
chakrit '26年

2
値を返そうとしているのに、なぜ割り当てが必要なのですか?
Matt Ellen

遅延初期化?
chakrit
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.