2010-01-07 13 views
32

संभव डुप्लिकेट शून्य पर:
C# okay with comparing value types to nullतुलना structs

मैं एक बहु-क्रम वातावरण में एक विंडोज़ अनुप्रयोग पर काम कर रहा था और कभी कभी अपवाद "Invoke या BeginInvoke नियंत्रण पर नहीं कहा जा सकता मिलेगा जब तक खिड़की हैंडल नहीं बनाया गया है। " तो मुझे लगा कि मैं बस कोड की इस पंक्ति को जोड़ूंगा:

if(this.Handle != null) 
{ 
    //BeginInvokeCode 
} 

लेकिन इससे समस्या हल नहीं हुई। तो मैंने थोड़ा आगे खोला, और महसूस किया कि IntPtr (जिस प्रकार फॉर्म। हैंडल है) एक ऐसी संरचना है जो शून्य नहीं हो सकती है। यह ठीक था जो काम करता था:

if(this.Handle != IntPtr.Zero) 
{ 
    //BeginInvokeCode 
} 

तो फिर यह मुझे मारा, जब मैं इसे शून्य के लिए जांच रहा था तब भी संकलन क्यों किया? तो मैं इसे अपने आप कोशिश करने का फैसला किया:

public struct Foo { } 

और उसके बाद:

static void Main(string[] args) 
    { 
     Foo f = new Foo(); 
     if (f == null) { } 
    } 

और पर्याप्त यकीन है कि यह कह रही है कि "त्रुटि 1 ऑपरेटर '==' प्रकार के ऑपरेंड के लिए लागू नहीं किया जा सकता संकलन नहीं किया 'ConsoleAplication1.Foo' और '' '। ठीक है, तो मैंने IntPtr के लिए मेटाडेटा को देखना शुरू कर दिया और IntPtr संरचना (ISerializable, ComVisible) में मौजूद मेरी Foo संरचना में सब कुछ जोड़ना शुरू कर दिया लेकिन कुछ भी मदद नहीं की। ! अंत में, जब मैं == के ऑपरेटर ओवरलोडिंग जोड़ा गया है और =, यह काम किया:

[Serializable] 
[ComVisible(true)] 
public struct Foo : ISerializable 
{ 
    #region ISerializable Members 

    public void GetObjectData(SerializationInfo info, StreamingContext context) 
    { 
     throw new NotImplementedException(); 
    } 

    #endregion 

    public override bool Equals(object obj) 
    { 
     return base.Equals(obj); 
    } 

    public override int GetHashCode() 
    { 
     return base.GetHashCode(); 
    } 

    public static bool operator ==(Foo f1, Foo f2) { return false; } 
    public static bool operator !=(Foo f1, Foo f2) { return false; } 
} 

यह अंत में संकलित:

static void Main(string[] args) 
    { 
     Foo f = new Foo(); 
     if (f == null) { } 
    } 

मेरा प्रश्न क्यों है? क्यों आप ओवरराइड == और! = क्या आपको शून्य से तुलना करने की अनुमति है? == और! = के पैरामीटर अभी भी प्रकार के फू हैं जो शून्य नहीं हैं, तो यह अचानक क्यों अनुमति देता है?

+2

अच्छे प्रश्न btw के लिए +1, और साथी ब्रुकलिनसाइट से हैलो :) –

उत्तर

14

यह मुद्दा की तरह लग रहा है कि जब एमएस नल प्रकार की शुरुआत की है, वे इसे ताकि हर struct परोक्ष अपने नल का प्रकार (foo?) को convertable है बनाया है, तो कोड

if(f == null) 

के बराबर है है
if ((Nullable<foo>)f == (Nullable<foo>)null) 

के बाद से MSDN कहा गया है कि, "किसी भी उपयोगकर्ता परिभाषित ऑपरेटरों कि मूल्य प्रकार के लिए मौजूद हैं भी नल प्रकार के द्वारा इस्तेमाल किया जा सकता" जब आप operator== ओवरराइड, आपको लगता है कि निहित डाली संकलित करने के लिए, के रूप में अब आप एक उपयोगकर्ता-निर्धारित की अनुमति == - आपको मुफ्त में बेकार अधिभार दे रहा है।

एक तरफ:

ldc.i4.0 
ldc.i4.0 
ceq 
stloc.1 //where there is an unused boolean local 

:

अपने उदाहरण में की तरह लगता है, वहाँ कुछ संकलक अनुकूलन केवल बात यह है कि संकलक भी संकेत है कि एक परीक्षण किया गया था द्वारा उत्सर्जित होता है है इस आईएल है ध्यान दें कि यदि आप मुख्य रूप से

Foo f = new Foo(); 
object b = null; 
if (f == b) { Console.WriteLine("?"); } 

यह अब संकलित नहीं है। लेकिन अगर आप struct बॉक्स:

Foo f = new Foo(); 
object b = null; 
if ((object)f == b) { Console.WriteLine("?"); } 

अगर compiles, उत्सर्जन करता है आईएल, और अपेक्षा के अनुरूप रन (struct अशक्त नहीं है);

2

मेरा मानना ​​है कि जब आप एक ऑपरेटर को अधिभारित करते हैं तो आप स्पष्ट रूप से इस धारणा की सदस्यता ले रहे हैं कि आप विशिष्ट ऑपरेटर के साथ आवश्यक सभी तर्कों को संभालेंगे। इसलिए ऑपरेटर अधिभार विधि में शून्य को संभालने की आपकी ज़िम्मेदारी है, अगर यह कभी भी हिट हो जाती है। इस मामले में मुझे यकीन है कि आपने शायद देखा है कि ओवरलोडेड विधियों को कभी भी हिट नहीं होने पर हिट नहीं किया जाता है।

वास्तव में दिलचस्प है कि Henks answer here के बाद, मैंने परावर्तक में निम्नलिखित कोड की जांच की।

Foo f1 = new Foo(); 
if(f1 == null) 
{ 
    Console.WriteLine("impossible"); 
} 

Console.ReadKey(); 

यह परावर्तक दिखाता है।

Foo f1 = new Foo(); 
Console.ReadKey(); 

कंपाइलर इसे साफ़ करता है और इसलिए ओवरलोडेड ऑपरेटर विधियों को कभी भी कॉल नहीं किया जाता है।

+0

... बेशक कि एक संरचना पैरामीटर वास्तव में शून्य नहीं होगा, इसलिए उसका प्रश्न। –

+0

सही है, लेकिन मुझे नहीं लगता कि संकलक इसका ख्याल रखता है, जिसका अर्थ है कि संकलक का संबंध है कि आप ऑपरेटर को ओवरलोड कर चुके हैं। इसके अलावा विधि को कभी भी बुलाया नहीं जाता है। –

-1

मैं उन पृष्ठों में देखने के लिए सिफारिश:

http://www.albahari.com/valuevsreftypes.aspx

http://msdn.microsoft.com/en-us/library/s1ax56ch.aspx

http://msdn.microsoft.com/en-us/library/490f96s2.aspx

+2

मुझे नहीं लगता कि उसे उसमें से किसी को भी पढ़ने की जरूरत है, यही वह सवाल नहीं है जिसके बारे में उसका सवाल था। –

1

struct भार के "==" को परिभाषित नहीं करता या "! =" यही कारण है कि आपको मूल त्रुटि मिली है। एक बार ओवरलोड को आपकी संरचना में जोड़ा गया था तो तुलना कानूनी थी (एक संकलक संभावित से)। ऑपरेटर अधिभार के निर्माता के रूप में यह तर्क इस संभाल को संभालने की आपकी ज़िम्मेदारी है (स्पष्ट रूप से माइक्रोसॉफ्ट ने इस मामले में इसे याद किया)।

आपकी संरचना के कार्यान्वयन (और यह क्या दर्शाता है) के आधार पर शून्य की तुलना पूरी तरह मान्य हो सकती है, यही कारण है कि यह संभव है। से यह करने में सक्षम है का चयन करने के

public static bool operator ==(object o1, object o2) 

और

public static bool operator ==(Foo f1, Foo f2) 

और दोनों के साथ कि:

+0

नहीं, हम इसे याद नहीं करते थे। (हालांकि तथ्य यह है कि एक चेतावनी रिपोर्ट नहीं की गई है, यह एक बग प्रतीत होता है।) यह व्यवहार विनिर्देश के अनुसार सही है। –

+0

@Eric, जानना अच्छा है। धन्यवाद –

3

सभी मुझे लगता है कि कर सकते हैं कि == ऑपरेटर के अपने से अधिक भार संकलक के बीच एक विकल्प देता है बाईं ओर ऑब्जेक्ट करने के लिए और पूर्व का उपयोग करें। निश्चित रूप से यदि आप अपने कोड के आधार पर कुछ चलाने की कोशिश करते हैं, तो यह आपके ऑपरेटर ओवरलोड में कदम नहीं उठाता है। ऑपरेटरों के बीच कोई विकल्प नहीं होने के साथ, कंपाइलर स्पष्ट रूप से कुछ और जांच कर रहा है।

+0

ध्यान दें कि अधिभार के बिना, पहला फॉर्म अभी भी लागू होगा - लेकिन संकलक इसे प्रतिबंधित करता है। मुझे लगता है कि आप निश्चित रूप से सही लाइनों के साथ हैं, लेकिन यहां गहरा जादू है। –

+2

पसंद वास्तव में (ओबीजे, ओबीजे), (फू, फू) और (फू ?, फू?) के बीच है - एक समानता ऑपरेटर को परिभाषित करना (फू, फू) स्वचालित रूप से उठाए गए टूल्स संस्करण को भी परिभाषित करता है। –

+0

@Eric - सही स्पष्टीकरण, कुल समझ में आता है। धन्यवाद। –

5

इसका सीरियलाइजेशन या COM के साथ कुछ लेना देना नहीं है - इसलिए समीकरण से इसे हटाने के लायक है। उदाहरण के लिए, यहाँ एक छोटा लेकिन पूरा कार्यक्रम है जो समस्या को दर्शाता है है:

using System; 

public struct Foo 
{ 
    // These change the calling code's correctness 
    public static bool operator ==(Foo f1, Foo f2) { return false; } 
    public static bool operator !=(Foo f1, Foo f2) { return false; } 

    // These aren't relevant, but the compiler will issue an 
    // unrelated warning if they're missing 
    public override bool Equals(object x) { return false; } 
    public override int GetHashCode() { return 0; } 
} 

public class Test 
{ 
    static void Main() 
    { 
     Foo f = new Foo(); 
     Console.WriteLine(f == null); 
    } 
} 

मेरा मानना ​​है कि इस संकलित वहाँ शून्य से एक अंतर्निहित रूपांतरण Nullable<Foo> को शाब्दिक है और आप इस कानूनी तौर पर कर सकते हैं क्योंकि:

Foo f = new Foo(); 
Foo? g = null; 
Console.WriteLine(f == g); 

यह दिलचस्प है कि यह तब होता है जब == ओवरलोड हो जाता है - मार्क ग्रेवेल ने इससे पहले देखा है। मुझे नहीं पता कि यह वास्तव में एक कंपाइलर बग है, या रूपांतरण, अधिभार इत्यादि के तरीके में बस कुछ सूक्ष्म है।

में कुछ मामलों (जैसे int, decimal) संकलक आप अंतर्निहित रूपांतरण के बारे में चेतावनी देगा - लेकिन दूसरों (जैसे Guid) में ऐसा नहीं है।

+0

@ जोन, मुझे इस व्यवहार को अनुकरण करने के लिए समान या GetHashCode को ओवरराइड करने की आवश्यकता नहीं थी। –

+1

@ स्टैन: नहीं, मैं सिर्फ चेतावनी के बिना संकलित करना चाहता था। वास्तव में जवाब में संपादित करने के बारे में था :) –

संबंधित मुद्दे