ヌル結合演算子を使用したヌルオブジェクトのインスタンス化


12

次の典型的なシナリオを検討してください。

if(myObject == null) {
    myObject = new myClass();
}

null合体演算子を使用した次の置換についてはどう思われますか?

myObject = myObject ?? new myClass();

2番目のフォームを使用する必要があるかどうかはわかりません。それはいい速記のmyObject = myObjectように思えますが、最初の構造は少しコード臭いのようです。

これは合理的なことですか、それとも私が見逃しているより良い略記ですか?または、「3行です、乗り越えてください!」

編集:前述したように、おそらくこれを典型的なシナリオと呼ぶのは誇張の言葉です。私は通常、まだ参照されている場合とされていない場合がある子参照型プロパティを持つデータベースからエンティティを取得するときに、この状況に遭遇することを発見します。

myClass myObject = myClassService.getById(id);
myObject.myChildObject = myObject.myChildObject ?? new myChildClass();

15
初心者はこのようなことを「ハッキング」と信じており、経験豊富な開発者は単に「イディオマティック」と呼んでいます。
Doc Brown

オブジェクトが値オブジェクト(イディオム語で「値セマンティクスを持っている」)のように見える場合、そのタイプの値のメンバーをMyClass提供する必要public static readonlyがありEmptyます。String.EmptyMSDNを参照してください。
rwong


5
??=演算子はありませんか?
アビブ

1
@avivあったらいいのに!
ローレンペクテル

回答:


16

私は常にヌル合体演算子を使用しています。私はそれの簡潔さが好きです。

この演算子は、本質的に三項演算子(A?B:C)に似ていることがわかります。それを読むのが第二の性質である前に少し練習が必要ですが、一度慣れると、手書き版より読みやすさが向上すると感じます。

また、ここで説明する状況は、オペレーターが役立つ1つのシナリオにすぎません。次のような構造を置き換えることも便利です。

if (value != null)
{
    return value;
}
else
{ 
    return otherValue;
}

または

return value != null ? value : otherValue;

return value ?? otherValue;

この演算子の使用について一般的に明確に同意します。ただし、それを使用することへの参照を見ると、通常はのようなものですmyObject = myObject2 ?? myObject3。私が具体的に疑問に思っているのは、ヌルでない限り、演算子を使用してオブジェクトをそれ自体に設定することです。
grin0048

2
どちらの場合にも同じ理由が当てはまると思います。同じロジックを表現するより簡潔な方法です。
17年

これら3つのフォームのそれぞれについてILを一度見たことを覚えています。1つ目と2つ目は同一でしたが、3つ目はnull比較であると思われる方法で最適化されました。
ジェシーC.スライサー

2

私が具体的に疑問に思っているのは、演算子がヌルでない限り、オブジェクトをそれ自体に設定することです。

?? operatorヌル合体演算子と呼ばれ、null許容値型または参照型のデフォルト値を定義するために使用されます。オペランドがnullでない場合、左側のオペランドを返します。それ以外の場合は、正しいオペランドを返します。

myObject = myObject ?? new myObject(); - instantiate default value of object

null結合演算子に関する詳細とコードサンプル-MSDNの記事

more than nullable条件を確認する場合は、代わりに三項演算子を使用できます。

三項演算子

?:Operatorもご覧ください。三項演算子または条件演算子と呼ばれます。条件演算子(?:)は、ブール式の値に応じて2つの値のいずれかを返します。

NULL可能型には値を含めることも、未定義にすることもできます。?? 演算子は、null許容型が非null許容型に割り当てられたときに返されるデフォルト値を定義します。??を使用せずに、null許容値タイプを非null許容値タイプに割り当てようとした場合 演算子を使用すると、コンパイル時エラーが生成されます。キャストを使用し、null許容値タイプが現在未定義の場合、InvalidOperationException例外がスローされます。

MSDNのコード例-?:演算子(C#リファレンス)

int? input = Convert.ToInt32(Console.ReadLine());
string classify;

// ?: conditional operator.
classify = (input.HasValue) ? ((input < 0) ? "negative" : "positive") : "undefined";

したがって、質問から例を取り、条件演算子を適用すると、次のようになりmyObject = (myObject == null) ? new myClass() : myObjectます。したがって、条件演算子のより良い使用法を逃していない限り、オブジェクトをそれ自体に設定する(それがnullでなければ)null合体演算子と同じ「問題」があるようです-そしてそれは簡潔ではありません。
grin0048

複数の条件(非ヌルでゼロより大きい)をチェックしている場合-条件演算子はそれを行います。ただし、nullチェックだけが機能しますか?オペレーター。
ユスボフ

2

私はそのシナリオが典型的であるとは思いません(少なくともそうあるべきです)。

一部のフィールドのデフォルト値をにしたくないnull場合は、フィールド初期化子に設定します。

myClass myObject = new myClass();

または、初期化がより複雑な場合は、コンストラクターで設定します。

myClass実際に必要なときにだけ作成したい場合(作成に時間がかかるなど)、次を使用できますLazy<T>

Lazy<myClass> myObject = new Lazy<myClass>();

(これはデフォルトのコンストラクターを呼び出します。初期化がより複雑な場合は、作成myClassするラムダをLazy<T>コンストラクターに渡します。)

値にアクセスするには、を使用しmyObject.Valueます。これは、初めてアクセスする場合に初期化を呼び出しますmyObject.Value


IMOレイジー作成は、結果のオブジェクトが必要であることが保証されていない場合にのみ役立ちます。レイジー作成を使用するのは、キャッシュされた派生結果があり、何かが変更されたときにリセットし、同じ結果が多く必要になることがわかっている場合のみですそして、再計算は高価です。それ以外の場合は、nullチェックに煩わされたくない
ラチェットフリーク

@ratchetfreakはい、それが最初にフィールド初期化子を提案した理由です。
svick

他のケースもあります。私が今見ているもの:最初のパスは例外ケースを設定します。2番目のパスは、他のすべてのデフォルトを設定します。?? =は行を半分にカットします。
ローレンペクテル

1

??オプションのメソッドパラメータと組み合わせて使用することが特に好きです。オーバーロードの必要性を制限し、物に価値があることを確認します。

public void DoSomething (MyClass thing1 = null) {
    thing1 = thing1 ?? new MyClass();
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.