मामले में किसी को भी एक Nullable वस्तु में struct मुक्केबाजी के प्रदर्शन हिट के बारे में सोच रहा है (is
से डबल प्रकार की जांच और कलाकारों से बचने के लिए), वहाँ एक गैर नगण्य भूमि के ऊपर है।
टीएल; डी: इस परिदृश्य में 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
चेतावनी: यह परीक्षण, कई मायनों में दोषपूर्ण हो सकता है, हालांकि इस बात की पुष्टि की थी कि बेंचमार्क कोड में ही एक अजीब फैशन में अनुकूलित नहीं किया गया था।
आईएल को देखते हुए, डबल-चेक विधि थोड़ा क्लीनर संकलित करती है।
मुक्केबाजी आईएल:
.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
दो बार जांचें आईएल:
.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
एक गलती है कि वास्तव में मुझे अच्छे लग रहे बनाने नहीं किया गया था खोलना के लिए रोमन रेनर को प्रॉप्स।
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
}
}
या मेरी पसंदीदा - अभिव्यक्ति शरीर समारोह के रूप में एक ही:
बस एक ध्यान दें कि आप .Net में mutable structs से बचने के लिए प्रोत्साहित किया जाता है। यह स्थापित है कि आपको अधिकांश समय संदर्भ प्रकार (कक्षाएं) से चिपकना चाहिए, और केवल कुछ ही सूत्रों का उपयोग करना चाहिए। –
मैं दूसरा हूं। * उपप्रकारों के बिना अपरिवर्तनीय structs * का प्रयोग करें। फिर बराबर और == किसी दिए गए रिसीवर (बाएं तरफ मूल्य) के लिए समान होना चाहिए जहां कार्यान्वयन में एकमात्र अंतर बराबर है, 'सरल' की आवश्यकता होती है और फिर, सादगी के लिए, प्रेषण == पर होती है। इस प्रकार दोनों अनुबंध पूरा हो गए हैं और आश्चर्य कम हो गए हैं। –
हां, यह संरचना अपरिवर्तनीय है। मैं केवल एक int की तुलना कर रहा हूँ। –