オブジェクトを値で比較するときに値型を表す.NETFrameworkの構造体(構造体のインスタンス)の特性については、すでに分析しました。
ここで、特定の例でこのプロセスを説明し、オブジェクトの比較の使用を一般的に値で判断できるかどうかを確認し、オブジェクトを値で比較するサンプル(参照を表すクラスインスタンス)を簡略化します。タイプ。
PersonStruct構造:
using System; namespace HelloEquatable { public struct PersonStruct : IEquatable<PersonStruct>, IEquatable<PersonStruct?> { private static int GetHashCodeHelper(int[] subCodes) { int result = subCodes[0]; for (int i = 1; i < subCodes.Length; i++) result = unchecked(result * 397) ^ subCodes[i]; return result; } private static string NormalizeName(string name) => name?.Trim() ?? string.Empty; private static DateTime? NormalizeDate(DateTime? date) => date?.Date; public string FirstName { get; } public string LastName { get; } public DateTime? BirthDate { get; } public PersonStruct(string firstName, string lastName, DateTime? birthDate) { this.FirstName = NormalizeName(firstName); this.LastName = NormalizeName(lastName); this.BirthDate = NormalizeDate(birthDate); } public override int GetHashCode() => GetHashCodeHelper( new int[] { this.FirstName.GetHashCode(), this.LastName.GetHashCode(), this.BirthDate.GetHashCode() } ); public static bool Equals(PersonStruct first, PersonStruct second) => first.BirthDate == second.BirthDate && first.FirstName == second.FirstName && first.LastName == second.LastName; public static bool operator ==(PersonStruct first, PersonStruct second) => Equals(first, second); public static bool operator !=(PersonStruct first, PersonStruct second) => !Equals(first, second); public bool Equals(PersonStruct other) => Equals(this, other); public static bool Equals(PersonStruct? first, PersonStruct? second) => first == second; // Alternate version: //public static bool Equals(PersonStruct? first, PersonStruct? second) => // first.HasValue == second.HasValue && // ( // !first.HasValue || Equals(first.Value, second.Value) // ); public bool Equals(PersonStruct? other) => this == other; // Alternate version: //public bool Equals(PersonStruct? other) => // other.HasValue && Equals(this, other.Value); public override bool Equals(object obj) => (obj is PersonStruct) && Equals(this, (PersonStruct)obj); // Alternate version: //public override bool Equals(object obj) => // obj != null && // this.GetType() == obj.GetType() && // Equals(this, (PersonStruct)obj); } }
ご覧のとおり、構造体のインスタンスはnullではなく、ユーザー定義の構造体から継承することはできないため、この例は構造体によって小さくて簡単です。前回の記事で、クラスインスタンスの値による比較を実装するための特性についてはすでに説明しました。
さらに、オブジェクト比較用のフィールドを決定し、GetHashCode()メソッドを実装しました。
比較の方法と演算子は、次の順序で実装されています。
- 構造体の2つのインスタンスを比較するために、PersonStruct.Equals(PersonStruct、PersonStruct)静的メソッドを実装しました。このメソッドは、他のメソッドや演算子を実装する際の参照比較メソッドとして使用します。さらに、演算子をサポートしていない言語の構造体のインスタンスを比較するために適用できます。
- PersonStruct。==(PersonStruct、PersonStruct)およびPersonStruct。!=(PersonStruct、PersonStruct)演算子も実装されています。 C#コンパイラには次の特性があることに注意してください。
- Nullable(Of T)のオーバーロードされた演算子T。==(T、T)およびT。!=(T、T)と比較できます
- 値が等しいかどうかをチェックする前に、コンパイラは構造体のインスタンスに有効な値があるかどうかを確認する場合があります。さらに、コンパイラは構造体のインスタンスをオブジェクトにラップしません。
- したがって、Nullable(Of T)構造体のインスタンスを型指定されていないnullable値と比較すると、Nullable(オーバーロードされた演算子のないT)構造体T。==(T、T)およびT。!=(T、T)は、Object。==(Object、Object)またはObject。!=(Object、Object)演算子を呼び出します。その結果、インスタンスをオブジェクトにラップします。
- PersonStruct.Equals(PersonStruct)メソッド(IEquatable(Of PersonStruct)の実装)は、PersonStruct.Equals(PersonStruct、PersonStruct)メソッドを呼び出すことによって実装されました。
- 構造体のインスタンスがオブジェクトにラップされないようにするために、1つまたは2つのNullable(Of PersonStruct)インスタンスがある場合、次のメソッドを実装できます。
- PersonStruct.Equals(PersonStruct?、PersonStruct?)は、PersonStruct。==(PersonStruct、PersonStruct)演算子の呼び出しとして、両方の引数の構造体のインスタンスをオブジェクトにラップしてObject.Equals(引数の少なくとも1つがNullable(Of PersonStruct)インスタンスである場合は、Object、Object)メソッド。さらに、このメソッドを使用して、演算子をサポートしていない言語のNullable(Of PersonStruct)インスタンスを比較できます。コードには、C#コンパイラがNullable(Of T)引数。
- PersonStruct.Equals(PersonStruct?)– Nullable(Of PersonStruct)引数をオブジェクトにラップしてPersonStruct.Equals(Object)メソッドを呼び出さないようにするために使用されるIEquatable(Of PersonStruct?)インターフェイスの実装。これは、PersonStruct。==(PersonStruct、PersonStruct)演算子の呼び出しとして実装され、コメント付きのコードを使用して、Nullable(Of T)に対してT。==(T、T)およびT。!=(T、T)演算子を使用します。 )引数。
- PersonStruct.Equals(Object)– Object.Equals(Object)メソッドをオーバーライドします。これは、引数をPersonStructにキャストし、PersonStruct.Equals(PersonStruct、PersonStruct)を呼び出すことにより、is演算子を使用して、引数タイプと現在のオブジェクトのタイプとの互換性をチェックすることによって実装されます。
注:
- IEquatable(Of PersonStruct?)— IEquatable(Of Nullable(Of PersonStruct))インターフェースの実装は、インスタンスをオブジェクトにラップすることが予想よりも速く発生する構造体を操作するときに、プラットフォームの特定の問題を示すのに役立ちます。 >
- 実際のプロジェクトでは、パフォーマンスを向上させる必要がない限り、アーキテクチャ上の理由からIEquatable(Of Nullable(Of T))の実装は適用できません。どのタイプのTタイプにも型付きIEquatableを実装しないでください。
- 一般に、さまざまな最適化でコードを圧倒する必要はありません。
構造体の場合、ユーザー定義の構造体の継承を回避し、nullのオブジェクトをチェックする必要があるため、値による比較をはるかに簡単で生産的にすることができます。さらに、Nullable(Of T)引数をサポートする新しいロジックを監視できます。
今後の出版物では、次の点を要約します。
- 値によるオブジェクトの比較を実装することをお勧めする場合;
- オブジェクト(参照型を表すクラスインスタンス)の値による比較の実装を簡素化する方法。
また読む:
値によるオブジェクトの比較。パート1:はじめに
値によるオブジェクトの比較。パート2:Equalsメソッドの実装ノート
値によるオブジェクトの比較。パート3:タイプ固有の等式および等式演算子
値によるオブジェクトの比較。パート4:継承と比較演算子
値によるオブジェクトの比較。パート5:構造の平等の問題