2010-03-30 26 views
49

में बराबर विधि को ओवरराइड करना मैंने structs के लिए दिशानिर्देशों को ओवरराइड करने की तलाश की है, लेकिन मुझे लगता है कि कक्षाओं के लिए मैं सब कुछ ढूंढ सकता हूं।स्ट्रक्चर

पहले मैंने सोचा कि मुझे यह देखने की आवश्यकता नहीं है कि पास ऑब्जेक्ट शून्य था, क्योंकि structs मूल्य प्रकार हैं और शून्य नहीं हो सकते हैं। लेकिन अब मैं इसके बारे में सोचने के लिए आते हैं, के रूप में बराबर होती हस्ताक्षर है

public bool Equals(object obj) 

ऐसा लगता है मेरी struct के उपयोगकर्ता को रोकने एक मनमाना संदर्भ प्रकार के साथ तुलना करने की कोशिश कर के लिए कुछ भी नहीं है।

मेरा दूसरा बिंदु मेरी संरचना में मेरे निजी क्षेत्रों की तुलना करने से पहले कास्टिंग I (मुझे लगता है) से संबंधित है। मैं ऑब्जेक्ट को अपने स्ट्रक्चर के प्रकार में कैसे डालना चाहता हूं? सी # as कीवर्ड केवल संदर्भ प्रकारों के लिए उपयुक्त लगता है।

public bool Equals(object obj) 
{ 
    if (obj is MyStruct) 
    { 
    var o = (MyStruct)obj; 
    ... 
    } 
} 
+6

बस एक ध्यान दें कि आप .Net में mutable structs से बचने के लिए प्रोत्साहित किया जाता है। यह स्थापित है कि आपको अधिकांश समय संदर्भ प्रकार (कक्षाएं) से चिपकना चाहिए, और केवल कुछ ही सूत्रों का उपयोग करना चाहिए। –

+4

मैं दूसरा हूं। * उपप्रकारों के बिना अपरिवर्तनीय structs * का प्रयोग करें। फिर बराबर और == किसी दिए गए रिसीवर (बाएं तरफ मूल्य) के लिए समान होना चाहिए जहां कार्यान्वयन में एकमात्र अंतर बराबर है, 'सरल' की आवश्यकता होती है और फिर, सादगी के लिए, प्रेषण == पर होती है। इस प्रकार दोनों अनुबंध पूरा हो गए हैं और आश्चर्य कम हो गए हैं। –

+0

हां, यह संरचना अपरिवर्तनीय है। मैं केवल एक int की तुलना कर रहा हूँ। –

उत्तर

64
struct MyStruct 
{ 
    public override bool Equals(object obj) 
    { 
     if (!(obj is MyStruct)) 
      return false; 

     MyStruct mys = (MyStruct) obj; 
     // compare elements here 

    } 

} 
+1

क्या आप स्पष्टता के लिए यहां 'ओवरराइड' कीवर्ड जोड़ सकते हैं? जावा में हमेशा अच्छा अभ्यास, सी # में समान होना चाहिए। –

+1

माइक्रोसॉफ्ट के दिशानिर्देश भी देखें - http://msdn.microsoft.com/en-us/library/ms173147(v=vs.80).aspx – yoyo

+7

@ जोहान्स इन सी # यह सिर्फ अच्छा अभ्यास से अधिक है, अगर आप 'ओवरराइड' छोड़ देते हैं विधि वास्तव में कुछ अलग करती है। – Pharap

5

is ऑपरेटर का प्रयोग करें।

यदि आप संलग्न करते हैं तो आपके पास अभी भी शून्य मूल्य हो सकते हैं? के रूप में उल्लेख struct नाम के बाद (यह हर मूल्य वस्तु के लिए काम करता है)

int? 

कास्टिंग (MyStructName)variableName

0

मौजूदा उत्तर देने के लिए जोड़ा जा रहा है:

+2

आप कर सकते हैं, लेकिन नलिकाओं के पास एक बहुत ही उच्च प्रदर्शन जुर्माना है जो कि "है" के बजाय "as" का उपयोग करके प्राप्त होने वाले किसी भी लाभ से अधिक होगा। –

+0

@ डैनस्टोरी मैं इतना तेज़ नहीं होगा। यदि आप [यह] (http://stackoverflow.com/a/28281410) पर एक नज़र डालने की परवाह करते हैं, तो मुझे यह जानकर उत्सुकता होगी कि क्या कुछ भी है जो मुझे याद आया है। tl; dr: है + कास्ट वास्तव में थोड़ा अच्छा है, लेकिन ऐसा लगता है कि "बहुत उच्च प्रदर्शन जुर्माना" जैसे + मुक्केबाजी के रूप में कुछ भी नहीं दिखता है। असल में, मैं + + कास्ट विश्वसनीय रूप से तेज़ नहीं कर सकता (कभी-कभी + मुक्केबाजी विधि लीड लेती है)। – tne

+0

@DanStory मैं उस पिछली टिप्पणी के बारे में सबसे निश्चित रूप से गलत था। दंड * उच्च * है (वैसे भी एक माइक्रोबेंमार्क में विकल्प की तुलना में)। वही जुड़ा हुआ उत्तर संपादित किया गया था। – tne

12

फोन करके भी किया जाता है मुझे लगता है, अगर एक नेट 4.5 उपयोग कर रहा है, एक डिफ़ॉल्ट कार्यान्वयन का उपयोग कर सकते documentation में:

जब आप अपना खुद का प्रकार परिभाषित करते हैं, तो उस प्रकार के आधार प्रकार के बराबर विधि द्वारा परिभाषित कार्यक्षमता प्राप्त होती है।

ValueType.Equals: मूल्य समानता; या तो प्रतिबिंब का उपयोग कर सीधे बाइट-बाय-बाइट तुलना या क्षेत्र-दर-क्षेत्र तुलना।

+1

यह वास्तव में 4.5 की पूर्व-तारीख है, मुझे नहीं पता कि यह कब जोड़ा गया था लेकिन यह निश्चित रूप से 4 में उपलब्ध है। हालांकि एमएसडीएन पर एक टिप्पणी यह ​​इंगित करती है कि यह फ़्लोटिंग पॉइंट प्रकारों के लिए सटीक नहीं हो सकता है। – Pharap

+1

प्रदर्शन विचारों के लिए http://stackoverflow.com/q/1009394 देखें। – tne

+0

@Pharap: 'बराबर' को परिभाषित करते समय, माइक्रोसॉफ्ट स्पष्ट करने में असफल रहा कि 'सत्य' वापस करने के लिए "बराबर" चीजें कैसे होनी चाहिए; कुछ संदर्भ हैं जहां * समकक्ष * के लिए फ़्लोटिंग-पॉइंट मानों का परीक्षण करना उपयोगी होता है (जो सकारात्मक और नकारात्मक शून्य संख्यात्मक रूप से बराबर हैं, यह दर्शाता है कि वे समकक्ष हैं, क्योंकि यदि एक्स और वाई समकक्ष हैं तो उन्हें 1/x = = 1/वाई, लेकिन यह सकारात्मक और नकारात्मक शून्य के बारे में सच नहीं है)। कुछ संरचनाओं में फ़्लोटिंग-पॉइंट मानों को समानता के लिए परीक्षण किया जाता है, लेकिन मुझे इस तरह के परीक्षण का अनुरोध करने के लिए कोई सामान्य माध्यम नहीं है। – supercat

6

मामले में किसी को भी एक 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 
    } 
} 

या मेरी पसंदीदा - अभिव्यक्ति शरीर समारोह के रूप में एक ही:

+0

आपका परीक्षण * दोषपूर्ण है! आपका बेंचमार्क 'Foo.Equals (Foo) 'विधि को कॉल कर रहा है। 'Foo.Equals (ऑब्जेक्ट)' कभी निष्पादित नहीं किया जाता है। –

+0

@RomanReiner: ओह, शर्म की बात है। मैं स्पष्ट रूप से वस्तुओं को कास्ट करने और भूल गया था; महान परिणामों के साथ (वास्तविक परिणाम बहुत अलग हैं) - ठीक है, अगर कोई माइक्रोवेन्चमार्क को किसी भी तरह से महत्व देता है। आपका बहुत बहुत धन्यवाद! – tne

1
some news in C# 7.0 को

धन्यवाद वहाँ एक आसान तरीका एक ही स्वीकार किए जाते हैं के रूप में जवाब पूरा करने के लिए है

struct MyStruct 
{ 
    public override bool Equals(object obj) => 
     obj is MyStruct mys 
      ? true // the initial "true" doesn't affect the overall boolean operation yet allows nice line aligning below 
       && this.field1 == mys.field1 
       && this.field2 == mys.field2 
      : false; // obj is not MyStruct 
} 
संबंधित मुद्दे