ステートメントを使用する3つの方法?.. C#6の間違い

C#6の安全なナビゲーション演算子( "?。"演算子)については既にご存じでしょう。これはかなり良い構文の砂糖ですが、この演算子の乱用のオプションを指摘したいと思います。



オプション1



フォーラムの1つで、C#を開発しているチームが「?。」を使用して振る舞うべきだと誰かが言った方法を読んだことを覚えています。 (nullの自動チェック)デフォルトでは、「。」で起こっていることの代わりにオブジェクト内をナビゲートするときの動作。 それについて少し話しましょう。 実際、「。」の実装を変更してみませんか? つまり、「?」を使用しないのはなぜですか。 万が一のために



コードの主な目的の1つは、 他の開発者に意図を開示することです。 演算子「?」の普及 「。」の代わりに 開発者は、コードが本当にnullを期待しているのか、それとも簡単に実行できるという理由だけでこのチェックを行っているのかを理解できない状況に陥ります。



public void DoSomething(Order order) { string customerName = order?.Customer?.Name; }
      
      





このメソッドは、orderパラメータがnullであることを本当に期待していますか? Customerプロパティもnullを返すことができますか? このコードは、yesを示しています。orderパラメーターとCustomerプロパティの両方をnullにすることができます。 しかし、著者が何をしているかを理解せずに、著者がこれらのチェックをそのように配置した場合、これはなくなります。



「?。」演算子を使用する それを本当に必要としないコードでは、混乱を招き、コードの可読性を低下させます



この問題は、C#にゼロ以外の参照型ないことに密接に関係しています。 言語レベルでコードを追加すると、コードを読むのがはるかに簡単になります。 さらに、このようなコードはコンパイルエラーにつながります。



 public void DoSomething(Order! order) { Customer customer = order?.Customer; // Compiler error: order can't be null }
      
      





オプション2



「?。」を悪用する別の方法 -不要な場合はnullに依存します。 例を考えてみましょう:



 List<string> list = null; int count; if (list != null) { count = list.Count; } else { count = 0; }
      
      





このコードには明らかな「におい」があります。 この場合、nullを使用する代わりに、 Nullオブジェクトパターンを使用することをお勧めします



 //   -    Null object List<string> list = new List<string>(); int count = list.Count;
      
      





これで新しい「?。」 演算子、「におい」はそれほど明白ではありません:



 List<string> list = null; int count = list?.Count ?? 0;
      
      





ほとんどの場合、Nullオブジェクトパターンはnullよりもはるかに望ましい選択です。 nullチェックを排除するだけでなく、コードでドメインモデルをより適切に表現するのにも役立ちます。 「?。」演算子を使用する nullチェックの排除には役立ちますが、コードで高品質のドメインモデルを構築する必要性を置き換えることはできません



新しい演算子を使用すると、同様のコードを非常に簡単に記述できます。



 public void Process() { int result = DoProcess(new Order(), null); } private int DoProcess(Order order, Processor processor) { return processor?.Process(order) ?? 0; }
      
      





Nullオブジェクトを使用してこのロジックを表現する方がより正確です。



 public void Process() { var processor = new EmptyProcessor(); int result = DoProcess(new Order(), processor); } private int DoProcess(Order order, Processor processor) { return processor.Process(order); }
      
      





オプション3



多くの場合、このコードはnew演算子の良い例として示されています。



 public void DoSomething(Customer customer) { string address = customer?.Employees ?.SingleOrDefault(x => x.IsAdmin)?.Address?.ToString(); SendPackage(address); }
      
      





このアプローチは実際にはメソッド内の行数を減らすことができますが、そのようなコード自体は許容できるものであることを意味します。



上記のコードはカプセル化原則に違反します 。 ドメインモデルの観点からは、別のメソッドを追加する方がはるかに適切です。



 public void DoSomething(Customer customer) { Contract.Requires(customer != null); string address = customer.GetAdminAddress(); SendPackage(address); }
      
      





この方法でカプセル化を維持し、ヌルチェックの必要性を排除します。 「?。」演算子を使用する カプセル化問題を隠すかもしれません 。 「?」を使用する誘惑に耐える方が良い そのような場合、メソッド呼び出しを次々と「連鎖」しても、非常に単純なタスクのように見えるかもしれません。



有効なユースケース



どのような場合に演算子「?」を使用しますか 許される? まず第一に、これはレガシーコードです。 アクセスできないコードまたはライブラリを使用する場合(またはソースに触れたくない場合)、このコードに適応する以外に選択肢はありません。 この場合、演算子「?。」 同様のコードベースで作業するために必要なコードの量を減らすことができます。



別の良い例は、イベント呼び出しです。



 protected void OnNameChanged(string name) { NameChanged?.Invoke(name); }
      
      





残りの例は、上記の無効な使用の3つのオプションに該当しないものに限定されます。



おわりに



演算子は「?。」 場合によってはコードの量を減らすのに役立ちます。また、コードなしでより明白になるかもしれないデザインの匂いを隠すこともできます。



特定のケースでこの演算子を使用するかどうかを決定するには、「古い方法」で記述されたコードが有効かどうかを考えてください。 その場合は、この演算子を使用してください。 そうでない場合は、コードをリファクタリングして、設計上の欠陥を削除してください。



記事の英語版: 「?。」の3つの誤用 C#6の演算子



All Articles