ディレクトリ
簡単な紹介
著書「一言で言えばC#の」見て最近は、.NET Frameworkはいくつかの欠点や短所がありますが、全体的にデザインはかなり良いですが、ことがわかります。ここでは、C#言語から言えば、関連する2つのオブジェクト間で比較していきます。
等しい比較値と基準タイプ
C#では、我々は、異なるタイプのデータのためのさまざまな方法を比較することを知っています。最も典型的には、比較値のタイプは、両方の値が同じであるが、参照型の複数が両方とも同じオブジェクトを参照するかどうかです。次の例では、あなたはそれら二つの違いを見ることができます。
int v1 = 3, v2 = 3;
object r1 = v1;
object r2 = v1;
object r3 = r1;
Console.WriteLine($"v1 is equal to v2: {v1 == v2}"); // true
Console.WriteLine($"r1 is equal to r2: {r1 == r2}"); // false
Console.WriteLine($"r1 is equal to r3: {r1 == r3}"); // true
この例では、タイプは、int
値のタイプ、その変数に属しv1
、及びv2
3です。あなたは結果の出力から見ることができ、二人は本当に等しいです。しかしのためobject
にもこの参照型、int
データの型からの変換(int
同じ参照番号ではなく、したがって、等しくないどちらもボックス化データ型)、(すなわち、行6)。しかし、のためにr3
、それへの参照されているr1
ので、参照オブジェクト、r3
およびr1
等しいです。
かどうかの基準に応じて参照型データと比較し、値によって型の比較の値ありません。しかし、いくつかの特別な事情があります。典型的な例では、文字列でありstring
、そしてSystem.Uri
。データ型のこれらの2つのタイプが参照型(本質的に全てのクラス)が、等価決意値の種類等を実証した上での結果です。
string s1 = "test";
string s2 = "test";
Uri u1 = new Uri("https://www.bing.com");
Uri u2 = new Uri("https://www.bing.com");
Console.WriteLine($"s1 is equal to s2: {s1 == s2}"); // true
Console.WriteLine($"u1 is equal to u2: {u1 == u2}"); // true
私たちは前に与えられたルールを破るために、これら2つのデータ型を見ることができます。けれどもstring
とSystem.Uri
二つのクラスの結果を比較似ていますが、行動の2つの具体的な実現は同じではありません。次いで、異なるデータ型のより具体的なプロセスは、後続のセクションで説明する方法、およびどのように比較例をカスタマイズすることです。しかし、我々は最初のC#で同じロジックに対処する方法を見て。
等価比較機能との関連
C#言語のシステムでは、クラスを知ることができObject
、ルートクラス全体のすべてのデータ型のを。.NETコア3.0からObject
見ることができ、同等の決意に関連付けられた機能は、クラスのメソッドのうちの2つのメンバー4を有し、以下のように、静的部材2は、方法です。
public virtual bool Equals(object? obj);
public virtual int GetHashCode();
public static bool ReferenceEquals(object? objA, object? objB);
public static bool Equals(object? objA, object? objB);
これとまったく同じではない他の情報、唯一の違いは、パラメータタイプが渡されることであることに留意されたいobject?
代わりobject
。これは主にC#8.0バージョンnull参照タイプであることができるが中に導入されます。ここでは、null参照の種類にはここに、この記事の焦点ではありませんすることができますすることができobject
に対処します。
ここでは、1つのこれらの4つの機能によって、1を導入されています。
- クラスのメンバメソッド
Equals
。この方法の役割は、契約が等しいと見なされる場合に現在使用されているオブジェクトと、着信オブジェクトは、比較することです。この方法は、設定されているvirtual
、すなわち、サブクラスでこのメソッドをオーバーライドすることができ、。 - クラスのメンバメソッド
GetHashCode
。この方法は、主に、ハッシュテーブルや辞書のクラスとして、ハッシュ処理に用いられます。この機能のために、それは2つのオブジェクトが等しいと見なされる場合、それらは同じハッシュ値を返しますが、基本的な要件です。異なるオブジェクトの場合、機能が必要異なるハッシュ値を返すことが必要ですが、ハッシュ処理の異なるデータオブジェクトを区別できるように、できるだけ多くの異なるハッシュ値に戻ることを望んでいません。上記の方法は、ので、virtual
キーワード変更、またサブクラスでオーバーライドすることができます。 - 静的メンバメソッド
ReferenceEquals
。この方法は主に2つのオブジェクトが同一の基準を指すかどうかを決定するために使用されます。では、ソースコードも、それは言っているその本質に見ることができますreturn objA == objB;
。このメソッドは静的であり、書き換えることはできませんので。 - 静的メンバメソッド
Equals
。この方法のために、ソースからも見ることができ、同一の第一同じではないに2つの参照かどうかが決定される、方法を使用して、オブジェクトがEquals
両方とも同じであるか否かを判定する。同様に、ので、この方法は静的であるが、書き換えることができません。
string
そして、System.Uri
等価比較
まあ、我々は戻って元の質問に来る、なぜstring
およびSystem.Uri
パフォーマンスの行動や他の参照型は同じではありませんが、同様の型と値。実際には、厳密に言えば、string
とSystem.Uri
同様の値の型でのパフォーマンスが、二つの内部の詳細は同じではありませんが、オブジェクト比較。
以下のためにstring
それは、ほとんどの場合、プログラムのコピーした文字列は、同じメモリアドレスへのすべての参照、限り、その値が同じであるように、どんなに多くの新しい文字列変数、一度保存されません。だから、まだ比較的引用されている文字列の比較のために、同じ値のほとんど同じオブジェクトへの参照です。
System.Uri
このようなオブジェクトのための異なるクラス、どのように多くのオブジェクトは、データを格納するためのオープンスペースとメモリのヒープ相当数に基づいて構築されます。しかし、比較して、第1の比較基準比較方法は、次いで、比較値を用います。両方ではない同じオブジェクトへの参照と、その後は、その値が(に等しい比較する場合には、あるソース)。
string s1 = "test";
string s2 = "test";
Uri u1 = new Uri("https://www.bing.com");
Uri u2 = new Uri("https://www.bing.com");
Console.WriteLine($"s1 is equal to s2 by the reference: {Object.ReferenceEquals(s1, s2)}"); // true
Console.WriteLine($"s1 is equal to s2: {s1 == s2}"); // true
Console.WriteLine($"u1 is equal to u2 by the reference: {Object.ReferenceEquals(u1, u2)}"); // false
Console.WriteLine($"u1 is equal to u2: {u1 == u2}"); // true
上記の例は、2つの文字列変数が同じデータオブジェクトを指すことがわかる(ReferenceEquals
この方法は、2つの参照を使用して、戻り値を確認することができ、同じオブジェクトを参照しているかどうかを決定することですtrue
)。以下のためにSystem.Uri
、2つの変数が同じオブジェクトを指していないが、両方の後続の決意が依然として等しい等しく、この時間は、両方が等しいかどうかを決定するための値に応じて、この時点で見ることができます。
ジェネリックインターフェイス IEquatable<T>
実質的に等しくすることによって二つのオブジェクトかどうかを上記の実施例、C#から分かるEquals
方法を決定します。しかし、Equals
この方法も、特にどのタイプの値に反映される万能薬ではありません。
以来Equals
メソッドタイプを渡されたパラメータが必要ですobject
。この方法は、型の値に適用された場合、それはに、キャストの型の値を引き起こしますobject
つまり一度(ボクシング)を梱包します、タイプ。ボクシングとアンボクシングは、一般的に多くの時間がかかり、効率が低下する傾向にあります。また、object
オブジェクトのタイプは、クラスが同じ判断及び他のクラスのオブジェクトであることができることを意味し、一般的には、我々は、オブジェクトは、2つのオブジェクトの前提と等しいかどうかを決定確かに同じクラスです。
採用C#ソリューションは、一般的なインターフェイスを使用することでIEquatable<T>
解決します。IEquatable<T>
これは次のように、二つの主な方法を含みます。
public interface IEquatable<T>
{
bool Equals(T other);
}
そしてObject.Equals(object? obj)
汎用関数法の内部に比べ、場合データ構造またはクラスは、次いで、インタフェースなどを実装したときに呼び出しEquals
たときに最も適切なタイプ、その最初の呼び出しの原理に従った方法IEquatable<T>
におけるEquals(T other)
方法。これは、値型の梱包作業を回避することができます。
カスタム比較方法
いくつかのケースでは、よりよい実世界のシナリオをシミュレートするために、私たちは二人の個人間の比較をカスタマイズする必要があります。このような比較の方法を達成するために、三つのステップを完了する必要が通常あります。
- 書き換え
Equals(object obj)
およびGetHashCode()
方法; - 過負荷演算子
==
と!=
; - 実現の
IEquatable<T>
方法。
最初のポイントでは、この2つの関数は書き直さなければなりません。以下のためにEquals(object obj)
ジェネリックインターフェイスメソッド内で実現場合はtrueを、メソッドを直接呼び出すことができる場所を考えることができます。GetHashCode()
異なるオブジェクトを区別するためなど、等しくなければならない2つのオブジェクトが等しい場合は、ハッシュ値ように、これは、ハッシュテーブルのパフォーマンスと辞書のクラスを有することになります。
最高の大型である必要は両方とも、第二及び第三の点のために、それは必要ではないが、一般に、より有効に利用するためです。
あなたが見ることができ、これらの3つの点が論理比較に関連しています。一般的に、私たちは、あなたができる汎用的なインタフェースでメソッドを呼び出すことにより、他の方法のための一般的なインタフェースでコアロジックを比較する傾向があります。
例えば
ここでは、小さな例を取ります。このシナリオを想像して、現在のマシンは、より多くの火の学習が、機械学習行列演算の話をせずに行うことはできません。行列のために、我々は保持するために2次元配列を使用することができます。数学の分野では、我々は2つの行列が等しいかどうかを決定し、二つの行列が等しいの各要素を決定することである、すなわち、判定方法の種類の値です。C#では、2次元配列のために参照型があり、あなたが直接、この目標を達成するために同等の決意を使用することはできません。したがって、我々は判断の彼らの方法を変更する必要があります。
public class Matrix : IEquatable<Matrix>
{
private double[,] matrix;
public Matrix(double[,] m)
{
matrix = m;
}
public bool Equals([AllowNull] Matrix other)
{
if (Object.ReferenceEquals(other, null))
return false;
if (matrix == other.matrix)
return true;
if (matrix.GetLength(0) != other.matrix.GetLength(0) ||
matrix.GetLength(1) != other.matrix.GetLength(1))
return false;
for (int row = 0; row < matrix.GetLength(0); row++)
for (int col = 0; col < matrix.GetLength(1); col++)
if (matrix[row,col] != other.matrix[row,col])
return false;
return true;
}
public override bool Equals(object obj)
{
if (!(obj is Matrix)) return false;
return Equals((Matrix)obj);
}
public override int GetHashCode()
{
int hashcode = 0;
for (int row = 0; row < matrix.GetLength(0); row++)
for (int col = 0; col < matrix.GetLength(1); col++)
hashcode = (hashcode + matrix[row, col].GetHashCode()) % int.MaxValue;
return hashcode;
}
public static bool operator == (Matrix m1, Matrix m2)
{
return Object.ReferenceEquals(m1, null) ? Object.ReferenceEquals(m2, null) : m1.Equals(m2);
}
public static bool operator !=(Matrix m1, Matrix m2)
{
return !(m1 == m2);
}
}
Matrix m1 = new Matrix(new double[,] { { 1, 2, 3 }, { 4, 5, 6 } });
Matrix m2 = new Matrix(new double[,] { { 1, 2, 3 }, { 4, 5, 6 } });
Console.WriteLine($"m1 is equal to m2 by the reference: {Object.ReferenceEquals(m1, m2)}"); // false
Console.WriteLine($"m1 is equal to m2: {m1 == m2}"); //true
比較ロジックが実装しますEquals(Matrix other)
。この方法では、マトリックスは、第1の2次元アレイを有する2つの参照は、ランクの数を決定した後、各要素の最終的な決意等しいか否かが判断されます。ここでは全体のコアロジック。以下のためのEquals(object obj)
十分==
かつ!=
直接呼び出しEquals(Matrix other)
方法。過負荷で、なお==
シンボルを直接使用することができない場合にm1==null
第1の目的は、空であるかどうかを決定するために、呼び出しそうでなければ無限ループ==
演算子オーバーロード機能。この機能で使用することができ、参照する必要の決意が必要でObject
、クラスの静的メソッドをReferenceEquals
決定すること。
概要
全体として、C#で同じ比較ルールへの参照である:値は、比較の値型が同じであり、参照型の比較は、両方が同じオブジェクトを参照するかどうかです。また、この記事では、これらのインタフェースの機能と効果は等価比較の枠組みを構築することで、関連する関数と等価とインターフェイスの一部が記載されています。これらの機能とインターフェイスは、デフォルトの比較規則を使用することができるだけでなく、我々はまた、ルールを比較するカスタマイズすることができます。前回の記事では、我々は、使用のカスタム比較ルールをシミュレートする例を与えます。この例では、我々は明らかにカスタム実装を比較参照することができます。