web-dev-qa-db-ja.com

C#とJavaが '=='のデフォルトとして参照等価を使用するのはなぜですか?

JavaとC#(そして他の言語も確かに)がデフォルトで_==_の等価性を参照するようになっている)理由をしばらく考えてきました。

私が行うプログラミングでは(確かにプログラミングの問題のごく一部にすぎません)、オブジェクトを比較する場合、参照の等価性ではなく論理的等価性がほとんど常に必要です。私はこれらの言語が両方ともこのルートを逆にして、_==_を論理的に等価にするのではなく、この方法をとった理由を考えようとしていて、参照の等価に.ReferenceEquals()を使用しました。

明らかに、参照の等価性を使用することは実装が非常に簡単であり、非常に一貫した動作を提供しますが、今日私が目にするほとんどのプログラミング方法にうまく適合していないようです。

私は論理比較を実装しようとする際の問題を知らないようにしたくないし、それをすべてのクラスで実装しなければならないことを望みません。また、これらの言語はずっと前に設計されたことにも気づきましたが、一般的な疑問が残っています。

これをデフォルトにすることの主な利点はありますか?

33
Zipper

Javaを実行したため、C#が実行しました。Javaは、Javaが演算子のオーバーロードをサポートしていないため、実行しました。値の等価性を再定義する必要があるためクラスごとに、演算子にすることはできませんでしたが、代わりにメソッドにする必要がありました。IMOこれは不適切な決定でした。a.equals(b)よりもa == bの書き込みと読み取りの両方がはるかに簡単で、プログラマにとってはるかに自然ですCまたはC++の経験がありますが、a == bはほとんどの場合間違っています。==の使用による.equalsが必要なバグにより、数千時間ものプログラマーの時間が無駄になりました。

30
kevin cline

短い答え:一貫性

しかし、あなたの質問に適切に答えるために、私は逆に一歩踏み出して、プログラミング言語の等しいことの意味の問題に目を向けることをお勧めします。少なくとも[〜#〜] 3つの[〜#〜]さまざまな可能性があり、さまざまな言語で使用されます。

  • Reference equality:aとbが同じオブジェクトを参照している場合、a = bがtrueであることを意味します。 aとbのすべての属性が同じであったとしても、aとbが異なるオブジェクトを参照している場合は当てはまりません。
  • Shallow equality:aとbが参照するオブジェクトのすべての属性が同一である場合、a = bがtrueであることを意味します。浅い等価性は、2つのオブジェクトを表すメモリ空間をビットごとに比較することで簡単に実装できます。参照の同等性は浅い同等性を意味することに注意してください
  • Deep equality:aとbの各属性が同一または非常に等しい場合、a = bがtrueであることを意味します。深い同等性は、参照の同等性と浅い同等性の両方によって暗示されることに注意してください。この意味で、深い平等は最も弱い平等であり、参照平等は最も強いものです。

これらの3つのタイプの等式は、実装が便利であるため、よく使用されます。3つの等式チェックはすべて、コンパイラーによって簡単に生成できます(深い等値の場合、コンパイラーは、タグビットを使用して、構造が循環参照があります)。しかし、別の問題があります。これらのどれも適切ではないかもしれません。

重要なシステムでは、オブジェクトの等価性は、深い等価性と参照の等価性の間の何かとして定義されることがよくあります。特定のコンテキストで2つのオブジェクトを等しいと見なしたいかどうかを確認するには、いくつかの属性をメモリ内のどこで比較するか、他の属性を完全に等しいかで比較する必要があるかもしれません。私たちが本当に望んでいるのは、「第4のタイプの平等」です。これは、文献でしばしば呼ばれる、本当に素敵なものですsemantic equality。私たちのドメインでは、物事が同じであれば、物事は同じです。 =)

だから私たちはあなたの質問に戻ることができます:

これをデフォルトに設定することには、私が単に見当たらないという主な利点がありますか、それともデフォルトの動作が論理的等価でなければならず、クラスに論理的等価が存在しない場合はデフォルトで参照等価に戻すのが妥当でしょうか?

「a == b」を任意の言語で書くとはどういう意味ですか?理想的には、常に同じである必要があります:セマンティックの平等。 しかし、それは不可能です。

主な考慮事項の1つは、少なくとも数値のような単純な型の場合、同じ値を割り当てた後、2つの変数が等しいことを期待することです。下記参照:

var a = 1;
var b = a;
if (a == b){
    ...
}
a = 3;
b = 3;
if (a == b) {
    ...
}

この場合、両方のステートメントで「aはbと等しい」と想定しています。それ以外は何でも正気ではありません。ほとんどの(すべてではないにしても)言語はこの規則に従います。したがって、単純型(別名値)を使用すると、セマンティックの等価性を実現する方法がわかります。オブジェクトの場合、それはまったく異なるものになる可能性があります。下記参照:

var a = new Something(1);
var b = a;
if (a == b){
    ...
}
b = new Something(1);
a.DoSomething();
b.DoSomething();
if (a == b) {
    ...
}

最初の「if」が常に真であると期待しています。しかし、2番目の「if」には何を期待しますか?それは本当に依存します。 「DoSomething」はaとbの(セマンティック)平等を変更できますか?

セマンティックの等価性の問題は、オブジェクトのコンパイラーによって自動的に生成できないこと、または割り当てから明らかでないことです。ユーザーがセマンティックの等価性を定義するためのメカニズムを提供する必要があります。オブジェクト指向言語では、そのメカニズムは継承されたメソッドequalsです。 OOコードの一部を読み取ると、メソッドがすべてのクラスでまったく同じ実装を持つとは限りません。継承とオーバーロードに慣れています。

ただし、演​​算子を使用すると、同じ動作が期待されます。 「a == b」と表示された場合、すべての状況で同じタイプの等式(上記の4から)を期待する必要があります。したがって、一貫性を目指して、言語設計者はすべての型に参照の等価性を使用しました。 プログラマーがメソッドをオーバーライドしたかどうかに依存するべきではありません。

PS:Dee言語は、JavaおよびC#とは少し異なります。equals演算子は、単純型の浅い等価性とユーザー定義クラスの意味的等価性を意味します(=演算を実装する責任はユーザー—デフォルトは提供されません。単純型の場合、浅い等価性は常にセマンティックな等価性であるため、言語は一貫しています。ただし、等しい演算子は、ユーザー定義型のデフォルトは未定義です。実装する必要があります。

16
Hbas

私はこれらの言語の両方がこのルートを逆にして、==を論理的に等価にするのではなく、この経路をたどる理由を考えていました。

後者のアプローチは混乱を招くためです。考慮してください:

if (null.ReferenceEquals(null)) System.out.println("ok");

このコードは"ok"、またはNullPointerExceptionをスローする必要がありますか?

0
Atsby