独習C#やいろんな記事を読んで自分なりにプログラムを書いて検証してみた。
以下を前提とした
- 同一性 参照が等しい場合true
- 同値性 値が等しい場合true
ミュータブルはEqualsで、イミュータブルでは==で基本は同値性の評価を行う。
StringBuilderクラス(ミュータブル)
==で同値性がfalseになる。
Equalsで同値性がtrueになる。
----StringBuilder----
stringbuild Equals 同値性
Console.WriteLine("----StringBuilder----");
var strbuild1 = new StringBuilder("stringbuild12");
var strbuild2 = new StringBuilder("stringbuild12");
if (strbuild1 == strbuild2) Console.WriteLine("stringbuild == 同値性");
if (strbuild1.Equals(strbuild2)) Console.WriteLine("stringbuild Equals 同値性");
Stringクラス(イミュータブル)
==で同値性がtrueになる。
Equalsで同値性がtrueになる。
----String----
string == 同値性
string Equals 同値性
var str1 = new String("string12");
var str2 = new String("string12");
if (str1 == str2) Console.WriteLine("string == 同値性");
if (str1.Equals(str2)) Console.WriteLine("string Equals 同値性");
では自作クラスでは?と素の自作クラスを作ってみた。
どちらも同値性はfalse
自作クラス
==で同値性がfalseになる。
Equalsで同値性がfalseになる。
==で同一性がtrueになる。
Equalsで同一性がtrueになる。
----自作 Class3 ----
jisaku ReferenceEquals(testClass3)31=33 同一性true
jisaku Equals(testClass3)31=33 同一性true
jisaku == testClass3 31=33 同一性true
Console.WriteLine("----自作 Class3 ----");
var testClass31 = new TesClass3(1,2);
var testClass32 = new TesClass3(1, 2);
var testClass33 = testClass31;
if (testClass31 == testClass32) Console.WriteLine("jisaku Class3 31== 同値性true");
if (testClass31.Equals(testClass32)) Console.WriteLine("jisaku Class3 Equals 同値性true");
if (object.ReferenceEquals(testClass31, testClass32)) Console.WriteLine("jisaku ReferenceEquals(testClass3)31=32 同一性true");
if (object.ReferenceEquals(testClass31, testClass33)) Console.WriteLine("jisaku ReferenceEquals(testClass3)31=33 同一性true");
if (testClass31.Equals(testClass33)) Console.WriteLine("jisaku Equals(testClass3)31=33 同一性true");
if (testClass31 == testClass33) Console.WriteLine("jisaku == testClass3 31=33 同一性true");
if (testClass31.Equals(testClass32)) Console.WriteLine("jisaku Equals(testClass3)31=33 同一性true");
if (testClass31 == testClass32) Console.WriteLine("jisaku == testClass3 31=33 同一性true");
internal class TesClass3
{
public double RealNumber { get; set; }
public double ImaginaryUnit { get; set; }
public TesClass3(double realNumber, double imaginaryUnit) => (RealNumber, ImaginaryUnit) = (realNumber, imaginaryUnit);
//public TesClass3(double realNumber) => RealNumber = realNumber;
}
ではどうすれば良いのと調べたら3つ案があったが、自作以外の2つの案にたどりついた
- IEquatableを継承する
- classをrecordに差し替えた案
自作クラスにIEquatableを継承してみた
==で同値性がtrueになる。
Equalsで同値性がtrueになる。
----自作クラスにIEquatable<TestClass1?>追加----
IEquatable<> 11 == 12 同値性true
IEquatable<> 11 Equals 12 同値性true
IEquatable<TestClass1?> ReferenceEquals(testClass2) 11=13 同一性true
Console.WriteLine("----自作クラスにIEquatable<TestClass1?>追加----");
var testClass11 = new TestClass1(1,2);
var testClass12 = new TestClass1(1, 2);
var testClass13 = testClass11;
if (testClass11 == testClass12) Console.WriteLine("IEquatable<> 11 == 12 同値性true");
if (testClass11.Equals(testClass12)) Console.WriteLine("IEquatable<> 11 Equals 12 同値性true");
if (object.ReferenceEquals(testClass11, testClass12)) Console.WriteLine("IEquatable<TestClass1?> ReferenceEquals(testClass2) 11=12 同一性true");
if (object.ReferenceEquals(testClass11, testClass13)) Console.WriteLine("IEquatable<TestClass1?> ReferenceEquals(testClass2) 11=13 同一性true");
internal class TestClass1 : IEquatable<TestClass1?>
{
public double RealNumber { get; set; }
public double ImaginaryUnit { get; set; }
public TestClass1(double realNumber, double imaginaryUnit) => (RealNumber, ImaginaryUnit) = (realNumber, imaginaryUnit);
public override bool Equals(object? obj)
{
return Equals(obj as TestClass1);
}
public bool Equals(TestClass1? other)
{
return other is not null &&
RealNumber == other.RealNumber &&
ImaginaryUnit == other.ImaginaryUnit;
}
public override int GetHashCode()
{
return HashCode.Combine(RealNumber, ImaginaryUnit);
}
public static bool operator ==(TestClass1? left, TestClass1? right)
{
return EqualityComparer<TestClass1>.Default.Equals(left, right);
}
public static bool operator !=(TestClass1? left, TestClass1? right)
{
return !(left == right);
}
}
自作クラスのclass をrecordに変えてみた。
==で同値性がtrueになる。
Equalsで同値性がtrueになる。
----自作クラス classをrecordに変更 Class2----
record 21 == 22 同値性true
record 21 Equals 22 同値性true
record ReferenceEquals(testClass2) 21=23 同一性true
Console.WriteLine("----自作クラス classをrecordに変更 Class2----");
TestClass2 testClass21 = new(1.0, 2);
TestClass2 testClass22 = new(1.0, 2);
TestClass2 testClass23= testClass21;
if (testClass21 == testClass22) Console.WriteLine("record 21 == 22 同値性true");
if (testClass21.Equals(testClass22)) Console.WriteLine("record 21 Equals 22 同値性true");
if (object.ReferenceEquals(testClass21, testClass22)) Console.WriteLine("record ReferenceEquals(testClass2) 21=22 同一性true");
if (object.ReferenceEquals(testClass21, testClass23)) Console.WriteLine("record ReferenceEquals(testClass2) 21=23 同一性tru
internal record TestClass2
{
public double RealNumber { get; init; }
public double ImaginaryUnit { get; init; }
public TestClass2(double realNumber, double imaginaryUnit) => (RealNumber, ImaginaryUnit) = (realNumber, imaginaryUnit);
}
== Equalsどちらも同値性はtrueになる。
しかもプロパティのsetを省略またはinitに変更しても同じ
結論
用意されているミュータブルはEqualsを使用する
用意されているイミュータブルは==を使用しする
自作クラスはIEquatableを継承またはclassをrecordに変えることにより、ミュータブル・イミュータブルに関係なく== Equalsどちらも使用可能。
プロジェクト単位で規則があればそれに従えばいいのだろうか?
参照先
==演算子とEqualsメソッドの違いとは?[C#]
.NET TIPS
https://atmarkit.itmedia.co.jp/ait/articles/1802/28/news028.html
zzzkan.me
C#でクラスあるいは構造体に値の等価性を定義する
https://zzzkan.me/blog/csharp-value-equality/
独習C# 第5版
https://www.shoeisha.co.jp/book/detail/9784798175560
[改訂新版]実戦で役立つ C#プログラミングのイディオム/定石&パターン
https://www.amazon.co.jp/dp/4297143070/ref=pd_lpo_d_sccl_1/358-2678425-2695319?psc=1