構造体のオーバーライドガイドラインを探しましたが、見つけることができるのはクラスだけです。
構造体は値型であり、nullにはできないため、最初は、渡されたオブジェクトがnullであるかどうかを確認する必要はないと考えました。しかし今、私はそれを考えるようになりました、等しい署名は
public bool Equals(object obj)
私の構造体のユーザーが任意の参照型と比較しようとするのを妨げるものは何もないようです。
2番目のポイントは、構造体のプライベートフィールドを比較する前に作成しなければならないキャストです。オブジェクトを構造体の型にキャストするにはどうすればよいですか? C#のas
キーワードは、参照型にのみ適しているようです。
struct MyStruct
{
public override bool Equals(object obj)
{
if (!(obj is MyStruct))
return false;
MyStruct mys = (MyStruct) obj;
// compare elements here
}
}
.NET4.5を使用している場合、 documentation に記載されているデフォルトの実装を使用できます。
独自の型を定義すると、その型はその基本型のEqualsメソッドで定義された機能を継承します。
ValueType.Equals:値が等しい;直接バイト単位の比較またはリフレクションを使用したフィールド単位の比較のいずれか。
C#7.0のいくつかのニュース のおかげで、受け入れられた答えと同じことを達成する簡単な方法があります。
struct MyStruct
{
public override bool Equals(object obj)
{
if (!(obj is MyStruct mys)) // type pattern here
return false;
return this.field1 == mys.field1 && this.field2 == mys.field2 // mys is already known here without explicit casting
}
}
または私のお気に入り-式の身体機能と同じ:
struct MyStruct
{
public override bool Equals(object obj) =>
obj is MyStruct mys
&& mys.field1 == this.field1
&& mys.field2 == this.field2;
}
(is
とキャストからの二重型チェックを回避するために)Nullableオブジェクトで構造体をボクシングすることのパフォーマンスヒットについて誰かが疑問に思う場合は、is無視できないオーバーヘッド。
tl; dr:このシナリオでは、is
を使用してキャストします。
struct Foo : IEquatable<Foo>
{
public int a, b;
public Foo(int a, int b)
{
this.a = a;
this.b = b;
}
public override bool Equals(object obj)
{
#if BOXING
var obj_ = obj as Foo?;
return obj_ != null && Equals(obj_.Value);
#Elif DOUBLECHECK
return obj is Foo && Equals((Foo)obj);
#Elif MAGIC
?
#endif
}
public bool Equals(Foo other)
{
return a == other.a && b == other.b;
}
}
class Program
{
static void Main(string[] args)
{
RunBenchmark(new Foo(42, 43), new Foo(42, 43));
RunBenchmark(new Foo(42, 43), new Foo(43, 44));
}
static void RunBenchmark(object x, object y)
{
var sw = Stopwatch.StartNew();
for (var i = 0; i < 100000000; i++) x.Equals(y);
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}
}
結果:
BOXING
EQ 8012 7973 7981 8000
NEQ 7929 7715 7906 7888
DOUBLECHECK
EQ 3654 3650 3638 3605
NEQ 3310 3301 3319 3297
警告:このテストには多くの点で欠陥があるかもしれませんが、ベンチマークコード自体が奇妙な方法で最適化されていないことを確認しました。
ILを見ると、ダブルチェックメソッドは少しきれいにコンパイルされます。
ボクシングIL:
.method public hidebysig virtual
instance bool Equals (
object obj
) cil managed
{
// Method begins at RVA 0x2060
// Code size 37 (0x25)
.maxstack 2
.locals init (
[0] valuetype [mscorlib]System.Nullable`1<valuetype StructIEqualsImpl.Foo> obj_
)
IL_0000: ldarg.1
IL_0001: isinst valuetype [mscorlib]System.Nullable`1<valuetype StructIEqualsImpl.Foo>
IL_0006: unbox.any valuetype [mscorlib]System.Nullable`1<valuetype StructIEqualsImpl.Foo>
IL_000b: stloc.0
IL_000c: ldloca.s obj_
IL_000e: call instance bool valuetype [mscorlib]System.Nullable`1<valuetype StructIEqualsImpl.Foo>::get_HasValue()
IL_0013: brfalse.s IL_0023
IL_0015: ldarg.0
IL_0016: ldloca.s obj_
IL_0018: call instance !0 valuetype [mscorlib]System.Nullable`1<valuetype StructIEqualsImpl.Foo>::get_Value()
IL_001d: call instance bool StructIEqualsImpl.Foo::Equals(valuetype StructIEqualsImpl.Foo)
IL_0022: ret
IL_0023: ldc.i4.0
IL_0024: ret
} // end of method Foo::Equals
ILを再確認します。
.method public hidebysig virtual
instance bool Equals (
object obj
) cil managed
{
// Method begins at RVA 0x2060
// Code size 23 (0x17)
.maxstack 8
IL_0000: ldarg.1
IL_0001: isinst StructIEqualsImpl.Foo
IL_0006: brfalse.s IL_0015
IL_0008: ldarg.0
IL_0009: ldarg.1
IL_000a: unbox.any StructIEqualsImpl.Foo
IL_000f: call instance bool StructIEqualsImpl.Foo::Equals(valuetype StructIEqualsImpl.Foo)
IL_0014: ret
IL_0015: ldc.i4.0
IL_0016: ret
} // end of method Foo::Equals
Roman Reinerに、私を本当に見栄えよくしない間違いを見つけてくれました。
is
演算子を使用します。
public bool Equals(object obj)
{
if (obj is MyStruct)
{
var o = (MyStruct)obj;
...
}
}
既存の回答に追加します。
?を追加すると、null許容値を持つことができます。構造体名の後(これはすべての値オブジェクトに対して機能します)
int?
キャストは(MyStructName)variableName
を呼び出すことでも行われます