मूल्य प्रकारों को शून्य करने के लिए C # ठीक है


85

मैं आज इस में भाग गया और पता नहीं क्यों C # संकलक एक त्रुटि नहीं फेंक रहा है।

Int32 x = 1;
if (x == null)
{
    Console.WriteLine("What the?");
}

मैं उलझन में हूँ कि कैसे x कभी संभवत: अशक्त हो सकता है। खासकर जब से यह काम निश्चित रूप से एक संकलक त्रुटि फेंकता है:

Int32 x = null;

क्या यह संभव है कि x शून्य हो सकता है, क्या Microsoft ने इस चेक को कंपाइलर में नहीं डालने का निर्णय लिया था, या यह पूरी तरह से याद नहीं था?

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

public class Test
{
    public DateTime ADate = DateTime.Now;

    public Test ()
    {
        Test test = new Test();
        if (test.ADate == null)
        {
            Console.WriteLine("What the?");
        }
    }
}

9
आप भी लिख सकते हैं if (1 == 2)। कोड पथ विश्लेषण करने के लिए यह संकलक का काम नहीं है; स्थैतिक विश्लेषण उपकरण और इकाई परीक्षण क्या हैं।
आरोनिट्यूड

चेतावनी क्यों चली गई, मेरा उत्तर देखें; और नहीं - यह एक अशक्त नहीं हो सकता।
मार्क Gravell

1
(1 == 2) पर सहमत, मैं स्थिति के बारे में अधिक सोच रहा था (1 == अशक्त)
जोशुआ बेल्डेन

सभी को धन्यवाद कि प्रतिक्रिया दी। सभी समझ में आता है।
जोशुआ बेल्डेन

चेतावनी या कोई चेतावनी के मुद्दे के बारे में: यदि प्रश्न में संरचना एक तथाकथित "सरल प्रकार" है, जैसे int, संकलक अच्छी चेतावनी उत्पन्न करता है। सरल प्रकार के लिए ==ऑपरेटर को सी # भाषा विनिर्देश द्वारा परिभाषित किया गया है। अन्य (सरल प्रकार नहीं) संरचनाओं के लिए, संकलक एक चेतावनी का उत्सर्जन करना भूल जाता है। देखें जब अशक्त करने के लिए struct की तुलना गलत संकलक चेतावनी जानकारी के लिए। उन संरचनाओं के लिए जो सरल प्रकार नहीं हैं, ==ऑपरेटर को एक opeartor ==विधि द्वारा अतिभारित किया जाना चाहिए जो संरचना का सदस्य है (अन्यथा कोई ==अनुमति नहीं है)।
जेपी स्टिग नीलसन

जवाबों:


119

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

इसी तरह, हम आपको "अगर (x == 12.6)" भी कहने की अनुमति देते हैं, जो हमेशा झूठ होगा। अंतर स्थानीय एक डबल में परिवर्तनीय है, शाब्दिक एक डबल में परिवर्तनीय है, और जाहिर है कि वे कभी भी समान नहीं होंगे।


4
: पुन अपनी टिप्पणी connect.microsoft.com/VisualStudio/feedback/...
मार्क Gravell

5
@ नाम: (मैं अपनी पहले की गलत टिप्पणी को वापस लेता हूं, जिसे मैंने हटा दिया है।) उपयोगकर्ता द्वारा परिभाषित मूल्य प्रकार जिनके पास उपयोगकर्ता द्वारा परिभाषित समानता ऑपरेटर है, जो डिफ़ॉल्ट रूप से परिभाषित है , उनके लिए उत्पन्न उपयोगकर्ता-परिभाषित समानता ऑपरेटर है। उठा हुआ उपयोगकर्ता-परिभाषित समानता ऑपरेटर आपके द्वारा बताए गए कारण के लिए लागू होता है: सभी मूल्य प्रकार स्पष्ट रूप से उनके संबंधित अशक्त प्रकार के लिए परिवर्तनीय होते हैं, जैसा कि अशक्त शाब्दिक है। ऐसा नहीं है कि उपयोगकर्ता-परिभाषित मूल्य प्रकार जिसमें उपयोगकर्ता परिभाषित तुलना ऑपरेटर की कमी है, अशक्त शाब्दिक के बराबर है।
एरिक लिपर्ट

3
@ नाम: निश्चित रूप से, आप अपने स्वयं के ऑपरेटर == और ऑपरेटर को लागू कर सकते हैं! = जो अशक्त संरचनाएं लेते हैं। यदि वे मौजूद हैं, तो संकलक उन्हें आपके लिए स्वचालित रूप से उत्पन्न करने के बजाय उनका उपयोग करेगा। (और संयोग से मुझे खेद है कि गैर-अशक्त ऑपरेंड पर अर्थहीन ऑपरेटर को चेतावनी देने से चेतावनी उत्पन्न नहीं होती है; यह उस कंपाइलर में एक त्रुटि है जिसे हमने फिक्सिंग के लिए नहीं किया है।)
एरिक लिपिपर्ट

2
हम अपनी चेतावनी चाहते हैं! हम इसके लायक हैं।
जेपी स्टिग नीलसन

3
@JamesDunne: static bool operator == (SomeID a, String b)इसे परिभाषित करने और इसके साथ टैग करने के बारे में क्या Obsolete? यदि दूसरा ऑपरेंड एक अनकैप्ड शाब्दिक है null, तो यह किसी भी रूप से बेहतर मैच होगा, जिसमें लिफ्ट ऑपरेटर्स के उपयोग की आवश्यकता होती है, लेकिन अगर ऐसा SomeID?होता है जो बराबर होता है null, तो उठा हुआ ऑपरेटर जीत जाएगा।
सुपरकैट

17

यह एक त्रुटि नहीं है, क्योंकि एक ( int?) रूपांतरण है; यह दिए गए उदाहरण में एक चेतावनी उत्पन्न करता है:

अभिव्यक्ति का परिणाम हमेशा 'गलत' होता है क्योंकि 'int' का मान कभी भी 'int' के 'null' के बराबर नहीं होता है?

यदि आप IL की जाँच करते हैं, तो आप देखेंगे कि यह पूरी तरह से पहुंच योग्य शाखा को हटा देता है - यह रिलीज़ बिल्ड में मौजूद नहीं है।

ध्यान दें कि यह समानता ऑपरेटरों के साथ कस्टम संरचनाओं के लिए यह चेतावनी उत्पन्न नहीं करता है। यह 2.0 में इस्तेमाल हुआ, लेकिन 3.0 संकलक में नहीं। कोड अभी भी हटा दिया गया है (इसलिए यह जानता है कि कोड उपलब्ध नहीं है), लेकिन कोई चेतावनी उत्पन्न नहीं होती है:

using System;

struct MyValue
{
    private readonly int value;
    public MyValue(int value) { this.value = value; }
    public static bool operator ==(MyValue x, MyValue y) {
        return x.value == y.value;
    }
    public static bool operator !=(MyValue x, MyValue y) {
        return x.value != y.value;
    }
}
class Program
{
    static void Main()
    {
        int i = 1;
        MyValue v = new MyValue(1);
        if (i == null) { Console.WriteLine("a"); } // warning
        if (v == null) { Console.WriteLine("a"); } // no warning
    }
}

आईएल के साथ (के लिए Main) - (जो दुष्प्रभाव हो सकता है) को छोड़कर सब कुछ नोट करें MyValue(1):

.method private hidebysig static void Main() cil managed
{
    .entrypoint
    .maxstack 2
    .locals init (
        [0] int32 i,
        [1] valuetype MyValue v)
    L_0000: ldc.i4.1 
    L_0001: stloc.0 
    L_0002: ldloca.s v
    L_0004: ldc.i4.1 
    L_0005: call instance void MyValue::.ctor(int32)
    L_000a: ret 
}

यह मूल रूप से है:

private static void Main()
{
    MyValue v = new MyValue(1);
}

1
किसी ने आंतरिक रूप से मुझे हाल ही में इसकी सूचना दी। मुझे नहीं पता कि हमने उस चेतावनी का उत्पादन क्यों बंद कर दिया। हमने इसे बग के रूप में दर्ज किया है।
एरिक लिपपर्ट 29:09


5

तथ्य यह है कि एक तुलना कभी सच नहीं हो सकती इसका मतलब यह नहीं है कि यह अवैध है। फिर भी, नहीं, मूल्य प्रकार कभी भी हो सकता है null


1
लेकिन एक मूल्य प्रकार के बराबर हो सकता है null। विचार करें int?, जो के लिए वाक्य रचना चीनी है Nullable<Int32>, जो एक मूल्य प्रकार है। एक प्रकार का चर int?निश्चित रूप से बराबर हो सकता है null
ग्रेग

1
@Greg: हाँ, यह शून्य के बराबर हो सकता है, यह मानते हुए कि आप जिस "बराबर" की बात कर रहे हैं, वह ==ऑपरेटर का परिणाम है । यह ध्यान रखना महत्वपूर्ण है कि उदाहरण वास्तव में अशक्त नहीं है , हालांकि।
एडम रॉबिन्सन

3

नहीं, Int32 xकभी नहीं बनेगा null

यदि आप किसी इंट की तुलना नल से कर रहे हैं तो तुलना करने वाला ऑपरेटर दो इंट ले लेता है।

"नल के साथ एक मूल्य प्रकार की तुलना एक चेतावनी क्यों है?" लेख आपकी मदद करेगा।


1

एक मान प्रकार नहीं हो सकता है null, हालांकि यह null(विचार Nullable<>) के बराबर हो सकता है । आपके मामले में intपरिवर्तनशील और nullअंतर्निहित रूप से Nullable<Int32>तुलना की जाती है।


0

मुझे संदेह है कि आपका विशेष परीक्षण केवल कंपाइलर द्वारा अनुकूलित किया जा रहा है जब यह आईएल उत्पन्न करता है क्योंकि परीक्षण कभी भी गलत नहीं होगा।

साइड नोट: Int32 में एक अशक्त Int32 का उपयोग करना संभव है? इसके बजाय एक्स।


0

मुझे लगता है कि यह है क्योंकि "==" एक वाक्यविन्यास चीनी है जो वास्तव में कॉल को System.Object.Equalsविधि का प्रतिनिधित्व करता है जो System.Objectपैरामीटर को स्वीकार करता है। ईसीएमए विनिर्देश द्वारा नल एक विशेष प्रकार है जो निश्चित रूप से प्राप्त होता है System.Object

इसलिए केवल एक चेतावनी है।


यह सही नहीं है, दो कारणों से। सबसे पहले, == में ऑब्जेक्ट के समान शब्दार्थ नहीं है। जब इसका एक तर्क एक संदर्भ प्रकार है। दूसरा, नल एक प्रकार नहीं है। यदि आप यह समझना चाहते हैं कि संदर्भ समानता ऑपरेटर कैसे काम करता है, तो विनिर्देश की धारा how. how.६ देखें।
एरिक लिपर्ट 29:09

"अशक्त शाब्दिक (§9.4.4.6) शून्य मान का मूल्यांकन करता है, जिसका उपयोग किसी वस्तु या सरणी पर इंगित न होने वाले संदर्भ को निरूपित करने के लिए किया जाता है, या किसी मान के अभाव के लिए। शून्य प्रकार का एकल मान होता है, जो अशक्त है। मूल्य। इसलिए एक अभिव्यक्ति जिसका प्रकार शून्य प्रकार है, केवल शून्य मान का मूल्यांकन कर सकता है। स्पष्ट रूप से शून्य प्रकार लिखने का कोई तरीका नहीं है और इसलिए, घोषित प्रकार में इसका उपयोग करने का कोई तरीका नहीं है। " - यह ईसीएमए का उद्धरण है। तुम्हारी किस बारे में बोलने की इच्छा थी? ECMA के किस संस्करण का आप उपयोग करते हैं? मुझे मेरा 7.9.6 नहीं दिखता।
विटाली

0

[संपादित करें: त्रुटियों में चेतावनी दी, और ऑपरेटरों को स्ट्रिंग हैक के बजाय अशक्त के बारे में स्पष्ट किया।]

उपरोक्त टिप्पणी में @ सुपरकैट के चतुर सुझाव के अनुसार, निम्न ऑपरेटर ओवरलोड आपको अपने कस्टम मूल्य प्रकार की तुलना शून्य करने की त्रुटि उत्पन्न करने की अनुमति देते हैं।

उन ऑपरेटरों को लागू करने से जो आपके प्रकार के अशक्त संस्करणों की तुलना करते हैं, तुलना में अशक्त का उपयोग ऑपरेटर के अशक्त संस्करण से मेल खाता है, जो आपको अप्रचलित विशेषता के माध्यम से त्रुटि उत्पन्न करने देता है।

जब तक Microsoft हमें अपना कंपाइलर चेतावनी वापस नहीं देता, तब तक मैं इस समाधान के साथ जा रहा हूँ, धन्यवाद @supercat!

public struct Foo
{
    private readonly int x;
    public Foo(int x)
    {
        this.x = x;
    }

    public override string ToString()
    {
        return string.Format("Foo {{x={0}}}", x);
    }

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

    public override bool Equals(Object obj)
    {
        return x.Equals(obj);
    }

    public static bool operator ==(Foo a, Foo b)
    {
        return a.x == b.x;
    }

    public static bool operator !=(Foo a, Foo b)
    {
        return a.x != b.x;
    }

    [Obsolete("The result of the expression is always 'false' since a value of type 'Foo' is never equal to 'null'", true)]
    public static bool operator ==(Foo a, Foo? b)
    {
        return false;
    }
    [Obsolete("The result of the expression is always 'true' since a value of type 'Foo' is never equal to 'null'", true)]
    public static bool operator !=(Foo a, Foo? b)
    {
        return true;
    }
    [Obsolete("The result of the expression is always 'false' since a value of type 'Foo' is never equal to 'null'", true)]
    public static bool operator ==(Foo? a, Foo b)
    {
        return false;
    }
    [Obsolete("The result of the expression is always 'true' since a value of type 'Foo' is never equal to 'null'", true)]
    public static bool operator !=(Foo? a, Foo b)
    {
        return true;
    }
}

जब तक मैं कुछ याद नहीं कर रहा हूं, तब तक आपका दृष्टिकोण कंपाइलर को कम करने का कारण होगा Foo a; Foo? b; ... if (a == b)..., भले ही ऐसी तुलना पूरी तरह से वैध हो। कारण मैंने "स्ट्रिंग हैक" का सुझाव दिया है कि यह उपरोक्त तुलना करने की अनुमति देगा लेकिन स्क्वॉक पर if (a == null)। उपयोग करने के बजाय string, कोई भी Objectया के अलावा किसी भी संदर्भ प्रकार को स्थानापन्न कर सकता है ValueType; यदि वांछित है, तो कोई एक निजी निर्माणकर्ता के साथ एक डमी वर्ग को परिभाषित कर सकता है जिसे कभी भी कॉल नहीं किया जा सकता है और इसे हकदार किया जा सकता है ReferenceThatCanOnlyBeNull
सुपरकैट

आप बिल्कुल सही हैं। मुझे स्पष्ट करना चाहिए कि मेरा सुझाव अशक्तियों के उपयोग को तोड़ता है ... जो कोडबेस में मैं काम कर रहा हूं उसे वैसे भी पापपूर्ण माना जाता है (अवांछित मुक्केबाजी, आदि)। ;)
योयो

0

मुझे लगता है कि सबसे अच्छा जवाब यह क्यों है कि कंपाइलर सामान्य कक्षाओं के लिए इसे स्वीकार करता है। निम्नलिखित वर्ग पर विचार करें ...

public class NullTester<T>
{
    public bool IsNull(T value)
    {
        return (value == null);
    }
}

यदि कंपाइलर nullमान प्रकारों के खिलाफ तुलनाओं को स्वीकार नहीं करता है , तो यह अनिवार्य रूप से इस वर्ग को तोड़ देगा, इसके प्रकार पैरामीटर से जुड़ा एक अंतर्निहित बाधा (यानी यह केवल गैर-मूल्य-आधारित प्रकारों के साथ काम करेगा)।


0

संकलक आपको ==अशक्त को लागू करने वाले किसी भी संरचना की तुलना करने की अनुमति देगा । यहां तक ​​कि यह आपको एक नल की तुलना करने की अनुमति देता है (आपको हालांकि एक चेतावनी मिलेगी)।

लेकिन यदि आप कोड को अलग करते हैं तो आप देखेंगे कि कोड संकलित होने पर तुलना हल हो रही है। इसलिए, उदाहरण के लिए, यह कोड (जहां Fooएक संरचना कार्यान्वयन है ==):

public static void Main()
{
    Console.WriteLine(new Foo() == new Foo());
    Console.WriteLine(new Foo() == null);
    Console.WriteLine(5 == null);
    Console.WriteLine(new Foo() != null);
}

यह IL उत्पन्न करता है:

.method public hidebysig static void  Main() cil managed
{
  .entrypoint
  // Code size       45 (0x2d)
  .maxstack  2
  .locals init ([0] valuetype test3.Program/Foo V_0)
  IL_0000:  nop
  IL_0001:  ldloca.s   V_0
  IL_0003:  initobj    test3.Program/Foo
  IL_0009:  ldloc.0
  IL_000a:  ldloca.s   V_0
  IL_000c:  initobj    test3.Program/Foo
  IL_0012:  ldloc.0
  IL_0013:  call       bool test3.Program/Foo::op_Equality(valuetype test3.Program/Foo,
                                                           valuetype test3.Program/Foo)
  IL_0018:  call       void [mscorlib]System.Console::WriteLine(bool)
  IL_001d:  nop
  IL_001e:  ldc.i4.0
  IL_001f:  call       void [mscorlib]System.Console::WriteLine(bool)
  IL_0024:  nop
  IL_0025:  ldc.i4.1
  IL_0026:  call       void [mscorlib]System.Console::WriteLine(bool)
  IL_002b:  nop
  IL_002c:  ret
} // end of method Program::Main

जैसा कि आप देख सकते हैं:

Console.WriteLine(new Foo() == new Foo());

इसका अनुवाद है:

IL_0013:  call       bool test3.Program/Foo::op_Equality(valuetype test3.Program/Foo,
                                                               valuetype test3.Program/Foo)

जहाँ तक:

Console.WriteLine(new Foo() == null);

असत्य में अनुवादित है:

IL_001e:  ldc.i4.0
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.