जब संभव हो तो अशक्त संदर्भ संभव नहीं है, तो हम क्यों संभव हो सकते हैं?


10

HNQ पर इस प्रश्न को पढ़ने के बाद , मैंने C # 8 में Nullable Reference Types के बारे में पढ़ा , और कुछ प्रयोग किए।

मुझे बहुत पता है कि 10 में से 9 बार या उससे भी अधिक बार, जब कोई कहता है कि "मुझे एक कंपाइलर बग मिला!" यह वास्तव में डिजाइन द्वारा, और उनकी अपनी गलतफहमी है। और जब से मैंने इस सुविधा को केवल आज ही देखना शुरू किया है, स्पष्ट रूप से मुझे इसकी बहुत अच्छी समझ नहीं है। इस तरह से, इस कोड को देखने की सुविधा देता है:

#nullable enable
class Program
{
    static void Main()
    {
        var s = "";
        var b = s == null; // If you comment this line out, the warning on the line below disappears
        var i = s.Length; // warning CS8602: Dereference of a possibly null reference
    }
}

मैंने ऊपर दिए गए दस्तावेज़ को पढ़ने के बाद, मुझे उम्मीद है कि s == nullलाइन मुझे एक चेतावनी देगी - आखिरकार sयह स्पष्ट रूप से गैर-अशक्त है, इसलिए इसकी तुलना करने से nullकोई मतलब नहीं है।

इसके बजाय, मुझे अगली पंक्ति पर एक चेतावनी मिल रही है , और चेतावनी कहती है कि sएक अशक्त संदर्भ संभव है, भले ही एक मानव के लिए, यह स्पष्ट है कि यह नहीं है।

अधिक से अधिक, अगर हम तुलना नहीं करते हैं तो चेतावनी प्रदर्शित नहीं की जाती sहै null

मैंने कुछ Googling किया और मैंने एक GitHub मुद्दे को मारा , जो पूरी तरह से कुछ और के बारे में निकला, लेकिन इस प्रक्रिया में मैंने एक योगदानकर्ता के साथ बातचीत की, जिसने इस व्यवहार में कुछ और अंतर्दृष्टि दी (उदाहरण के लिए "Null जाँच अक्सर एक उपयोगी तरीका है एक चर की अशक्तता के बारे में इसके पूर्व अनुमान को रीसेट करने के लिए संकलक के बारे में बताना। " )। यह अभी भी मुझे अनुत्तरित मुख्य प्रश्न के साथ छोड़ गया, हालांकि।

एक नया GitHub मुद्दा बनाने के बजाय, और संभवतः अविश्वसनीय रूप से व्यस्त परियोजना योगदानकर्ताओं का समय निकालकर, मैं इसे समुदाय के सामने रख रहा हूं।

क्या आप मुझे समझा सकते हैं कि क्या चल रहा है और क्यों? विशेष रूप से, s == nullलाइन पर कोई चेतावनी क्यों नहीं उत्पन्न होती है , और हमारे पास ऐसा क्यों है CS8602जब ऐसा नहीं लगता कि nullयहां संदर्भ संभव है? यदि अशक्तता इंजेक्शन बुलेट-प्रूफ नहीं है, जैसा कि लिंक किए गए गिटहब धागे से पता चलता है, यह कैसे गलत हो सकता है? इसके कुछ उदाहरण क्या होंगे?


ऐसा लगता है कि संकलक खुद ही व्यवहार निर्धारित करता है कि इस बिंदु पर चर "s" शून्य हो सकता है। वैसे भी, अगर मैं कभी तार या वस्तुओं का उपयोग करता हूं, तो फ़ंक्शन को कॉल करने से पहले हमेशा एक जांच होनी चाहिए। "s?। चलन" को चाल चलनी चाहिए और चेतावनी को खुद ही गायब कर देना चाहिए।
chg

1
@chg, कोई आवश्यकता नहीं होनी चाहिए ?क्योंकि sयह अशक्त नहीं है। यह अशक्त नहीं होता है, केवल इसलिए कि हम इसकी तुलना करने के लिए पर्याप्त रूप से मूर्ख थे null
एंड्रयू साविनिख

मैं पहले वाले सवाल का अनुसरण कर रहा था (क्षमा करें, यह नहीं मिल सकता है) जहां यह माना गया था कि यदि आप एक चेक जोड़ते हैं कि एक मूल्य शून्य है, तो संकलक इसे 'संकेत' के रूप में लेता है कि मान शून्य हो सकता है, भले ही वह हो कोई मामला नहीं है।
स्टुअर्टड

@stuartd, हाँ, ऐसा लगता है कि यह है। तो अब सवाल यह है कि यह उपयोगी क्यों है?
एंड्रयू सविनाख

1
@chg, ठीक है, जो मैं सवाल शरीर में कहता हूं, क्या यह नहीं है?
एंड्रयू सविनाख

जवाबों:


7

यह प्रभावी रूप से उस उत्तर का एक डुप्लिकेट है जो @stuartd से जुड़ा हुआ है, इसलिए मैं यहां सुपर डीप विवरण में नहीं जा रहा हूं। लेकिन इस मामले की जड़ यह है कि यह न तो एक भाषा बग है और न ही संकलक बग है, लेकिन यह वास्तव में व्यवहार के रूप में लागू किया गया है। हम एक चर की अशक्त स्थिति को ट्रैक करते हैं। जब आप शुरू में परिवर्तनशील घोषित करते हैं, तो वह स्थिति नोटनल है क्योंकि आप इसे स्पष्ट रूप से एक ऐसे मूल्य के साथ आरंभ करते हैं जो शून्य नहीं है। लेकिन हम ट्रैक नहीं करते हैं कि NotNull कहां से आया है। यह, उदाहरण के लिए, प्रभावी रूप से समकक्ष कोड है:

#nullable enable
class Program
{
    static void Main()
    {
        M("");
    }
    static void M(string s)
    {
        var b = s == null;
        var i = s.Length; // warning CS8602: Dereference of a possibly null reference
    }
}

दोनों ही मामलों में, आप स्पष्ट रूप से परीक्षण sकरते हैं null। हम इसे प्रवाह विश्लेषण के इनपुट के रूप में लेते हैं, जिस तरह से इस प्रश्न में उत्तर दिए गए मैड्स: https://stackoverflow.com/a/59328672/2672518 । उस उत्तर में, परिणाम यह है कि आपको रिटर्न पर चेतावनी मिलती है। इस मामले में, उत्तर यह है कि आपको एक चेतावनी मिलती है कि आपने संभवतः एक अशक्त संदर्भ दिया है।

यह अशक्त नहीं होता है, केवल इसलिए कि हम इसकी तुलना करने के लिए पर्याप्त रूप से मूर्ख थे null

हां, यह वास्तव में करता है। संकलक को। मनुष्य के रूप में, हम इस कोड को देख सकते हैं और स्पष्ट रूप से समझ सकते हैं कि यह एक शून्य संदर्भ अपवाद को नहीं फेंक सकता है। लेकिन संकलक में जिस तरह से अशक्त प्रवाह विश्लेषण लागू किया जाता है, वह नहीं हो सकता। हमने इस विश्लेषण में कुछ सुधारों पर चर्चा की जहां हम अतिरिक्त राज्यों को जोड़ते हैं जहां से मूल्य आया है, लेकिन हमने फैसला किया कि इसने कार्यान्वयन के लिए जटिलता का एक बड़ा सौदा जोड़ा, न कि बहुत बड़ा लाभ। यह इस तरह के मामलों के लिए उपयोगी होगा, जहां उपयोगकर्ता एक चर को newस्थिर मान के साथ शुरू करता है और फिर इसे nullवैसे भी जांचता है ।


धन्यवाद। यह ज्यादातर अन्य प्रश्नों के साथ समानता को संबोधित करता है, मैं मतभेदों को अधिक संबोधित करना चाहता हूं। उदाहरण के लिए, s == nullचेतावनी क्यों नहीं देता है?
एंड्रयू सविनाख

इसके अलावा, मैं सिर्फ महसूस किया कि साथ #nullable enable; string s = "";s = null;संकलन और काम करता है (यह अभी भी एक चेतावनी पैदा करता है) क्या फायदे हैं, कार्यान्वयन के लिए जो अशक्त एनोटेशन संदर्भ में "गैर-अशक्त संदर्भ" को अशक्त करने की अनुमति देता है?
एंड्रयू साविनिख

मैड का जवाब इस तथ्य पर केंद्रित है कि "[संकलक] अलग चर की स्थिति के बीच संबंध को ट्रैक नहीं करता है" हमारे पास इस उदाहरण में अलग-अलग चर नहीं हैं, इसलिए मुझे इस मामले के बाकी मैड के उत्तर को लागू करने में परेशानी होती है।
एंड्रयू साविनिख

यह बिना कहे चला जाता है, लेकिन याद रखना, मैं यहाँ आलोचना करने के लिए नहीं, बल्कि सीखने के लिए हूँ। मैं C # का उपयोग उस समय से कर रहा हूं जब यह पहली बार 2001 में सामने आया था। भले ही मैं भाषा के लिए नया नहीं हूं, लेकिन संकलक ने जिस तरह से व्यवहार किया वह मेरे लिए आश्चर्यजनक था। इस सवाल का लक्ष्य तर्क को झुकाव देना है कि यह व्यवहार मनुष्यों के लिए उपयोगी क्यों है।
एंड्रयू साविनिख

जाँच करने के लिए वैध कारण हैं s == null। शायद, उदाहरण के लिए, आप एक सार्वजनिक पद्धति में हैं, और आप पैरामीटर सत्यापन करना चाहते हैं। या, शायद आप एक पुस्तकालय का उपयोग कर रहे हैं जो गलत तरीके से एनोटेट किया गया है, और जब तक वे उस बग को ठीक नहीं करते हैं, आपको उस अशक्त से निपटना होगा जहां यह घोषित नहीं किया गया था। उन मामलों में से, अगर हमने चेतावनी दी, तो यह एक बुरा अनुभव होगा। असाइनमेंट की अनुमति के लिए: स्थानीय चर एनोटेशन सिर्फ पढ़ने के लिए हैं। वे रनटाइम को बिल्कुल भी प्रभावित नहीं करते हैं। वास्तव में, हम उन सभी चेतावनियों को एक त्रुटि कोड में डालते हैं ताकि यदि आप कोड मंथन कम करना चाहते हैं तो आप उन्हें बंद कर सकें।
333fred

0

यदि अशक्तता का अनुमान बुलेट-प्रूफ नहीं है, तो [..] यह गलत कैसे हो सकता है?

जैसे ही वे उपलब्ध थे मैंने खुशी से C # 8 के अशक्त संदर्भों को अपनाया। जैसा कि मुझे ReSharper के [NotNull] (आदि) नोटेशन का उपयोग करने के लिए इस्तेमाल किया गया था, मैंने दोनों के बीच कुछ मतभेदों को नोटिस किया।

सी # कंपाइलर को मूर्ख बनाया जा सकता है, लेकिन यह सावधानी के साथ (आमतौर पर, हमेशा नहीं) की ओर जाता है।

भविष्य के आगंतुकों के लिए एक संदर्भ के रूप में, ये ऐसे परिदृश्य हैं जिनके बारे में मैंने देखा कि कंपाइलर बहुत भ्रमित हैं (मुझे लगता है कि ये सभी मामले डिज़ाइन के अनुसार हैं):

  • अशक्त क्षमा करना । अक्सर डेरेक्शन की चेतावनी से बचने के लिए इस्तेमाल किया जाता है, लेकिन ऑब्जेक्ट को गैर-अशक्त बनाए रखा जाता है। ऐसा लगता है कि आप अपने पैरों को दो जूते में रखना चाहते हैं।
    string s = null!; //No warning

  • सतह विश्लेषण । ReSharper (जो कोड एनोटेशन का उपयोग करता है ) के विरोध में , C # कंपाइलर अभी भी अशक्त संदर्भों को संभालने के लिए विशेषताओं की एक पूरी श्रृंखला का समर्थन नहीं करता है।
    void DoSomethingWith(string? s)
    {    
        ThrowIfNull(s);
        var split = s.Split(' '); //Dereference warning
    }

हालांकि, यह अशक्तता की जांच के लिए कुछ निर्माण का उपयोग करने की अनुमति देता है जो चेतावनी से भी छुटकारा दिलाता है:

    public static void DoSomethingWith(string? s)
    {
        Debug.Assert(s != null, nameof(s) + " != null");
        var split = s.Split(' ');  //No warning
    }

या (अभी भी बहुत अच्छा) विशेषताएँ (उन सभी को यहाँ खोजें ):

    public static bool IsNullOrEmpty([NotNullWhen(false)] string? value)
    {
        ...
    }

  • अतिसंवेदनशील कोड विश्लेषण । यह वही है जिसे आपने प्रकाश में लाया है। कंपाइलर को काम करने के लिए धारणाएँ बनानी पड़ती हैं और कभी-कभी वे प्रति-सहज (मनुष्यों के लिए, कम से कम) लगते हैं।
    void DoSomethingWith(string s)
    {    
        var b = s == null;
        var i = s.Length; // Dereference warning
    }

  • जेनेरिक से परेशानीयहां पूछा गया और यहां बहुत अच्छी तरह से समझाया गया है (एक ही लेख पहले की तरह, पैराग्राफ "टी के साथ मुद्दा?")। पीढ़ी जटिल है क्योंकि उन्हें संदर्भ और मूल्य दोनों को खुश करना है। मुख्य अंतर यह है कि जबकि string?सिर्फ एक स्ट्रिंग है, int?एक बन जाता है Nullable<int>और संकलक को अलग-अलग तरीके से संभालने के लिए मजबूर करता है। यहाँ भी, संकलक सुरक्षित रास्ता चुन रहा है, आपको यह बताने के लिए मजबूर कर रहा है कि आप क्या उम्मीद कर रहे हैं:
    public interface IResult<out T> : IResult
    {
        T? Data { get; } //Warning/Error: A nullable type parameter must be known to be a value type or non-nullable reference type.
    }

हल देने वाली बाधाएं:

    public interface IResult<out T> : IResult where T : class { T? Data { get; }}
    public interface IResult<T> : IResult where T : struct { T? Data { get; }}

लेकिन अगर हम अड़चन का उपयोग नहीं करते हैं और 'हटा दें?' डेटा से, हम अभी भी 'डिफ़ॉल्ट' कीवर्ड का उपयोग करके इसमें शून्य मान डाल सकते हैं:

    [Pure]
    public static Result<T> Failure(string description, T data = default)
        => new Result<T>(ResultOutcome.Failure, data, description); 
        // data here is definitely null. No warning though.

आखिरी मुझे मुश्किल लगता है, क्योंकि यह असुरक्षित कोड लिखने की अनुमति देता है।

आशा है कि यह किसी की मदद करता है।


मैं सुझाव दूंगा कि अशक्त विशेषताओं पर डॉक्स दें: docs.microsoft.com/en-us/dotnet/csharp/nullable-attributes । वे आपके कुछ मुद्दों को हल करेंगे, विशेष रूप से सतह विश्लेषण अनुभाग और जेनरिक के साथ।
333 फ्रेड

लिंक @ 333fred के लिए धन्यवाद। हालांकि ऐसी विशेषताएँ हैं जिनके साथ आप खेल सकते हैं, एक विशेषता जो मेरे द्वारा पोस्ट की गई समस्या को हल करती है (कुछ जो मुझे बताता है कि ThrowIfNull(s);मुझे आश्वस्त कर रहा है जो sअशक्त नहीं है), मौजूद नहीं है। इसके अलावा लेख बताता है कि गैर- अशक्त जेनरिक को कैसे संभाला जाए , जबकि मैं दिखा रहा था कि आप संकलक को "मूर्ख" कैसे कर सकते हैं, एक अशक्त मूल्य है लेकिन इसके बारे में कोई चेतावनी नहीं है।
एल्विन सार्टोर

वास्तव में, विशेषता मौजूद है। मैंने इसे जोड़ने के लिए डॉक्स पर एक बग दर्ज किया। तुम खोज रहे हो DoesNotReturnIf(bool)
333fred

@ 333fred वास्तव में मुझे कुछ और पसंद है DoesNotReturnIfNull(nullable)
एल्विन सार्टोर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.