क्या अशक्त संदर्भ वास्तव में एक बुरी बात है?


161

मैंने सुना है कि प्रोग्रामिंग भाषाओं में अशक्त संदर्भों का समावेश "बिलियन डॉलर की गलती" है। पर क्यों? ज़रूर, वे NullReferenceException पैदा कर सकते हैं, लेकिन इतना क्या? यदि अनुचित तरीके से उपयोग किया जाता है तो भाषा का कोई भी तत्व त्रुटियों का स्रोत हो सकता है।

और विकल्प क्या है? यह कहने के बजाय मुझे लगता है:

Customer c = Customer.GetByLastName("Goodman"); // returns null if not found
if (c != null)
{
    Console.WriteLine(c.FirstName + " " + c.LastName + " is awesome!");
}
else { Console.WriteLine("There was no customer named Goodman.  How lame!"); }

आप यह कह सकते हैं:

if (Customer.ExistsWithLastName("Goodman"))
{
    Customer c = Customer.GetByLastName("Goodman") // throws error if not found
    Console.WriteLine(c.FirstName + " " + c.LastName + " is awesome!"); 
}
else { Console.WriteLine("There was no customer named Goodman.  How lame!"); }

लेकिन यह कैसे बेहतर है? किसी भी तरह से, यदि आप यह जांचना भूल जाते हैं कि ग्राहक मौजूद है, तो आपको एक अपवाद मिलेगा।

मुझे लगता है कि एक CustomerNotFoundException NullReferenceException की तुलना में अधिक वर्णनात्मक होने के कारण डिबग करना थोड़ा आसान है। क्या यही सबकुछ है इसमें है?


54
मैं "यदि ग्राहक मौजूद है तो ग्राहक से संपर्क करें" से सावधान रहें, जैसे ही आप कोड की दो लाइनें लिखते हैं, आप दौड़ की स्थिति के लिए दरवाजा खोल रहे हैं। जाओ, और फिर nulls / पकड़ने के अपवादों के लिए जाँच सुरक्षित है।
कार्सन 63000

26
यदि केवल हम सभी रनटाइम त्रुटियों के स्रोत को समाप्त कर सकते हैं, तो हमारा कोड सही होने की गारंटी होगी! यदि C # में NULL संदर्भ आपके मस्तिष्क को तोड़ते हैं, तो अमान्य प्रयास करें, लेकिन गैर-पूर्ण, C में संकेत;)
LnxPrgr3

हालांकि, कुछ भाषाओं में अशक्त सह-संचालन और सुरक्षित नेविगेशन ऑपरेटर हैं
jk।

35
null प्रति se खराब नहीं है। एक प्रकार की प्रणाली जो टाइप टी और टाइप टी + नल के बीच कोई अंतर नहीं करती है, खराब है।
इंगो

27
कुछ वर्षों बाद अपने स्वयं के प्रश्न पर वापस आ रहा हूं, अब मैं पूरी तरह से विकल्प / शायद दृष्टिकोण का एक रूपांतरण हूं।
टिम गुडमैन

जवाबों:


91

अशक्त बुराई है

इस विषय पर इन्फोक्यू पर एक प्रस्तुति है: नल संदर्भ: टोनी होरे द्वारा द बिलियन डॉलर मिस्टेक

विकल्प का प्रकार

कार्यात्मक प्रोग्रामिंग से विकल्प एक विकल्प प्रकार का उपयोग कर रहा है , जिसमें शामिल SOME valueया हो सकता है NONE

एक अच्छा लेख "विकल्प" पैटर्न जो विकल्प प्रकार पर चर्चा करता है और जावा के लिए इसे लागू करता है।

मैंने इस मुद्दे के बारे में जावा के लिए एक बग-रिपोर्ट भी पाया है: NullPointerException को रोकने के लिए जावा में अच्छा विकल्प प्रकार जोड़ेंअनुरोधित सुविधा जावा 8 में पेश किया गया था।


5
Nullable <x> C # में एक समान अवधारणा है, लेकिन विकल्प पैटर्न के कार्यान्वयन के लिए रास्ता कम हो जाता है। मुख्य कारण यह है कि .NET System.Nullable में मूल्य प्रकारों तक सीमित है।
मैटवेवी

27
विकल्प विकल्प के लिए +1। हास्केल की शायद से परिचित होने के बाद , नल अजीब लगने लगते हैं ...
एंड्रेस एफ।

5
डेवलपर कहीं भी त्रुटि कर सकता है, इसलिए अशक्त सूचक अपवाद के बजाय आपको खाली विकल्प अपवाद मिलेगा। पूर्व की तुलना में उत्तरार्द्ध कैसे बेहतर है?
ग्रीनल्डमैन

7
@greenoldman "खाली विकल्प अपवाद" जैसी कोई चीज नहीं है। NULL के रूप में परिभाषित डेटाबेस कॉलम में NULL मान सम्मिलित करने का प्रयास करें।
जोनास

36
@greenoldman नल के साथ समस्या यह है कि कुछ भी अशक्त हो सकता है, और एक डेवलपर के रूप में आपको अतिरिक्त सतर्क रहना होगा। एक Stringप्रकार वास्तव में एक स्ट्रिंग नहीं है। यह एक String or Nullप्रकार है। विकल्प प्रकारों के विचार का पूरी तरह से पालन करने वाली भाषाएं उनके प्रकार प्रणाली में अशक्त मूल्यों को अस्वीकार करती हैं, यह गारंटी देते हुए कि यदि आप एक प्रकार के हस्ताक्षर को परिभाषित करते हैं जिसके लिए एक String(या कुछ और) की आवश्यकता होती है , तो इसका एक मूल्य होना चाहिएOptionप्रकार वहाँ चीजें हैं जो एक प्रकार का स्तर के प्रतिनिधित्व दे रहा है या नहीं हो सकता है एक मूल्य है, तो आप जानते हैं कि जहां आप स्पष्ट रूप से उन स्थितियों को संभालने चाहिए।
KChaloux

127

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

आप सही कह रहे हैं कि सुशोभित त्रुटि हैंडलिंग कार्यात्मक रूप से शून्य-जाँच ifबयानों के समान हो सकती है । लेकिन क्या होता है जब आप अपने आप को आश्वस्त करते हैं कि संभवतः एक अशक्त नहीं हो सकता है, वास्तव में, एक अशक्त? Kerboom। आगे जो भी होता है, मैं शर्त लगाने को तैयार हूं कि 1) यह सुशोभित नहीं होगा और 2) आप इसे पसंद नहीं करेंगे।

और "डिबग करने में आसान" के मूल्य को खारिज न करें। परिपक्व उत्पादन कोड एक पागल, विशाल प्राणी है; जो कुछ भी आपको अधिक जानकारी देता है वह क्या गलत है और आपको खोदने के घंटों को कैसे बचा सकता है।


27
+1 आमतौर पर उत्पादन कोड में त्रुटियों की पहचान जितनी जल्दी हो सके करने की आवश्यकता होती है ताकि उन्हें ठीक किया जा सके (आमतौर पर डेटा त्रुटि)। डिबग करना जितना आसान है, उतना ही अच्छा है।

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

19
अस्पष्टीकृत बमों के लिए +1। अशक्त संदर्भों के साथ, कहने का public Foo doStuff()अर्थ यह नहीं है कि "सामान करें और फू परिणाम लौटाएं" इसका अर्थ है "सामान करें और फू परिणाम लौटाएं, या शून्य वापस लौटें, लेकिन आपको पता नहीं है कि ऐसा होगा या नहीं।" इसका परिणाम यह है कि आपको निश्चित होने के लिए EVERYWHERE की जांच करनी होगी। निरर्थक मूल्य को दर्शाने के लिए नल को हटा सकते हैं और एक विशेष प्रकार या पैटर्न (जैसे मूल्य प्रकार) का उपयोग कर सकते हैं।
मैट ओलेनिक

12
@ग्रीनॉल्डमैन, आपका उदाहरण हमारी बात को प्रदर्शित करने के लिए दोगुना प्रभावी है कि शब्दार्थ मॉडल के रूप में आदिम प्रकार (चाहे अशक्त या पूर्णांक) का उपयोग खराब अभ्यास है। यदि आपके पास एक ऐसा प्रकार है जिसके लिए ऋणात्मक संख्याएँ एक मान्य उत्तर नहीं है, तो आपको अर्थ के साथ अर्थ मॉडल को लागू करने के लिए नए प्रकार के वर्ग बनाने चाहिए। अब उस गैर-नकारात्मक प्रकार के मॉडलिंग को संभालने के लिए सभी कोड को उसकी कक्षा में स्थानीय कर दिया गया है, बाकी सिस्टम से अलग कर दिया गया है, और इसके लिए नकारात्मक मूल्य बनाने के किसी भी प्रयास को तुरंत पकड़ा जा सकता है।
हपरनिकेतेस

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

50

कोड में अशक्त संदर्भों का उपयोग करने के साथ कई समस्याएं हैं।

सबसे पहले, यह आमतौर पर एक विशेष राज्य को इंगित करने के लिए उपयोग किया जाता है । प्रत्येक राज्य के लिए एक नए वर्ग या स्थिरांक को परिभाषित करने के बजाय, जैसा कि विशिष्ट रूप से किया जाता है, अशक्त संदर्भ का उपयोग करते हुए एक हानिपूर्ण, बड़े पैमाने पर सामान्यीकृत प्रकार / मूल्य का उपयोग किया जाता है

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

तीसरा, अशक्त संदर्भ परीक्षण के लिए अतिरिक्त कोड पथ का परिचय देते हैं ।

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

पांचवां, भाषा का रनटाइम पहले से ही प्रकार की जाँच कर रहा है जब वह किसी ऑब्जेक्ट के मेथड टेबल पर सेलेक्टर लुकअप करता है। इसलिए आप जाँच कर प्रयास कर रहे हैं कि क्या ऑब्जेक्ट का प्रकार वैध / अमान्य है और फिर रनटाइम होने पर इस विधि को लागू करने के लिए मान्य ऑब्जेक्ट के प्रकार की जाँच करें।

रनटाइम की जांच का लाभ उठाने के लिए NullObject पैटर्न का उपयोग क्यों नहीं किया गया है ताकि यह उस राज्य के लिए विशिष्ट NOP विधियों को लागू कर सके (नियमित राज्य के इंटरफ़ेस के अनुरूप), जबकि आपके पूरे कोड में अशक्त संदर्भों के लिए सभी अतिरिक्त चेकिंग को समाप्त कर सकता है?

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


7
... क्योंकि पता लगाना कि किसने उत्पन्न किया (या बल्कि, बदलने या शुरू करने की जहमत नहीं उठाई) कचरा डेटा जो आपकी कथित उपयोगी वस्तु को भर रहा है, क्या NULL संदर्भ पर नज़र रखने की तुलना में बहुत आसान है? मैं इसे नहीं खरीदता, हालांकि मैं ज्यादातर सांख्यिकीय रूप से टाइप की गई भूमि में फंस गया हूं।
डैश-टॉम-बैंग

हालांकि, गार्बेज डेटा अशक्त संदर्भों की तुलना में बहुत दुर्लभ है। विशेष रूप से प्रबंधित भाषाओं में।
डीन हार्डिंग

5
-1 अशक्त वस्तु के लिए।
डेडएमजी जूल

4
अंक (4) और (5) ठोस हैं, लेकिन NullObject कोई उपाय नहीं है। आप और भी बुरे जाल में पड़ जाएंगे। जब आप मौजूदा मौजूदा स्थिति को जानते हैं, तो निश्चित रूप से, यह मदद कर सकता है, लेकिन इसे नेत्रहीन रूप से (नल के बजाय) उपयोग करने से आपको अधिक लागत आएगी।
ग्रीनल्डमैन

1
@ gnasher729, जबकि ऑब्जेक्टिव-सी का रनटाइम जावा और अन्य सिस्टम की तरह एक नल-पॉइंटर अपवाद को नहीं फेंकेगा, यह मेरे संदर्भ में उल्लिखित कारणों के लिए किसी भी बेहतर संदर्भ का उपयोग नहीं करता है। उदाहरण के लिए, यदि [self.navigationController.presentingViewController dismissViewControllerAnimated: YES completion: nil];रनटाइम में कोई नेविगेशनकंट्रोलर नहीं है, तो इसे नो-ऑप्स के अनुक्रम के रूप में माना जाता है, तो दृश्य को प्रदर्शन से हटाया नहीं जाता है, और आप एक्सकोड के सामने बैठकर घंटों तक अपना सिर खुजाने की कोशिश कर सकते हैं कि क्यों।
हपरनिकेटेस

48

नल इतने बुरे नहीं हैं, जब तक आप उनसे उम्मीद नहीं कर रहे हैं। आप चाहिए स्पष्ट रूप से जो एक भाषा-डिजाइन समस्या है कोड है कि आप अशक्त उम्मीद कर रहे हैं, में निर्दिष्ट करने के लिए की जरूरत है। इस पर विचार करो:

Customer? c = Customer.GetByLastName("Goodman");
// note the question mark -- 'c' is either a Customer or null
if (c != null)
{
    // c is not null, therefore its type in this block is
    // now 'Customer' instead of 'Customer?'
    Console.WriteLine(c.FirstName + " " + c.LastName + " is awesome!");
}
else { Console.WriteLine("There was no customer named Goodman.  How lame!"); }

यदि आप ए पर तरीकों को लागू करने की कोशिश करते हैं Customer?, तो आपको एक संकलन-समय त्रुटि मिलनी चाहिए। अधिक भाषाएं ऐसा नहीं करती हैं (IMO) यह है कि वे परिवर्तन के प्रकार के लिए प्रदान नहीं करते हैं यह किस गुंजाइश के आधार पर है। यदि भाषा इसे संभाल सकती है, तो समस्या पूरी तरह से हल हो सकती है। प्रकार प्रणाली।

विकल्प-प्रकार और Maybe, का उपयोग करके इस समस्या को संभालने का कार्यात्मक तरीका भी है , लेकिन मैं इससे परिचित नहीं हूं। मैं इस तरह से पसंद करता हूं क्योंकि सही कोड को सैद्धांतिक रूप से सही संकलन के लिए केवल एक वर्ण जोड़ा जाना चाहिए।


11
वाह, यह बहुत अच्छा है। संकलित समय में बहुत अधिक त्रुटियों को पकड़ने के अलावा, यह मुझे यह पता लगाने के लिए दस्तावेज़ीकरण की जांच करने से रोकता है कि क्या GetByLastNameरिटर्न शून्य है यदि नहीं मिला (जैसा कि अपवाद को फेंकने का विरोध किया गया है) - मैं सिर्फ यह देख सकता हूं कि यह वापस आता है Customer?या नहीं Customer
टिम गुडमैन

14
@JBRWilkinson: यह विचार दिखाने के लिए सिर्फ एक पकाया हुआ उदाहरण है, वास्तविक कार्यान्वयन का उदाहरण नहीं। मैं वास्तव में लगता है कि यह एक बहुत साफ विचार है।
डीन हार्डिंग

1
आप सही हैं कि यह अशक्त वस्तु पैटर्न नहीं है, हालांकि।
डीन हार्डिंग

13
यह ऐसा दिखता है जैसे हास्केल क्या कहता है Maybe- ए Maybe Customerया तो Just c(जहां cएक है Customer) या Nothing। अन्य भाषाओं में, इसे कहा जाता है Option- इस प्रश्न पर कुछ अन्य उत्तर देखें।
MatrixFrog

2
@SimonBarker: आप सुनिश्चित हो सकते हैं कि क्या Customer.FirstNameप्रकार का है String(जैसा कि विरोध किया गया है String?)। यही वास्तविक लाभ है - केवल वैरिएबल / गुण जिनके पास सार्थक कुछ भी नहीं है, को अशक्त घोषित किया जाना चाहिए।
योगू

20

वहाँ बहुत सारे उत्कृष्ट उत्तर हैं जो दुर्भाग्यपूर्ण लक्षणों को कवर करते हैं null, इसलिए मैं एक वैकल्पिक तर्क प्रस्तुत करना चाहता हूं: नल प्रकार प्रणाली में एक दोष है।

एक प्रकार की प्रणाली का उद्देश्य यह सुनिश्चित करना है कि प्रोग्राम के विभिन्न घटक "एक साथ फिट" ठीक से हों; एक अच्छी तरह से टाइप किए गए कार्यक्रम अपरिभाषित व्यवहार में "रेल बंद" नहीं कर सकते।

जावा की एक काल्पनिक बोली पर विचार करें, या जो कुछ भी आपकी पसंद की वैधानिक रूप से टाइप की गई भाषा है, जहां आप "Hello, world!"किसी भी प्रकार के किसी भी चर को स्ट्रिंग असाइन कर सकते हैं :

Foo foo1 = new Foo();  // Legal
Foo foo2 = "Hello, world!"; // Also legal
Foo foo3 = "Bonjour!"; // Not legal - only "Hello, world!" is allowed

और आप चर की जाँच कर सकते हैं जैसे:

if (foo1 != "Hello, world!") {
    bar(foo1);
} else {
    baz();
}

इस बारे में कुछ भी असंभव नहीं है - कोई ऐसी भाषा डिज़ाइन कर सकता है यदि वे चाहते थे। विशेष मूल्य की आवश्यकता नहीं है "Hello, world!"- यह संख्या 42, टपल (1, 4, 9), या, कह सकते हैं null। लेकिन आप ऐसा क्यों करेंगे? प्रकार का एक वैरिएबल Fooकेवल Foos धारण करना चाहिए - जो कि टाइप सिस्टम का पूरा बिंदु है! nullसे Fooअधिक नहीं "Hello, world!"है। इससे भी बदतर, किसी भी प्रकार nullका मूल्य नहीं है , और ऐसा कुछ भी नहीं है जो आप इसके साथ कर सकते हैं!

प्रोग्रामर कभी भी यह सुनिश्चित नहीं कर सकता है कि एक चर वास्तव में एक है Foo, और न ही कार्यक्रम कर सकता है; अपरिभाषित व्यवहार से बचने के लिए, "Hello, world!"उन्हें Fooएस के रूप में उपयोग करने से पहले चर की जांच करनी होगी । ध्यान दें कि पिछले स्निपेट में स्ट्रिंग चेक करने से इस तथ्य का प्रचार नहीं होता है कि foo1 वास्तव में एक है Foo- barसंभावना है कि इसकी स्वयं की जांच भी होगी, बस सुरक्षित रहने के लिए।

तुलना करें कि पैटर्न मिलान के साथ Maybe/ Optionप्रकार का उपयोग करें :

case maybeFoo of
 |  Just foo => bar(foo)
 |  Nothing => baz()

Just fooक्लॉज़ के अंदर , आप और प्रोग्राम दोनों यह सुनिश्चित करने के लिए जानते हैं कि हमारे Maybe Fooवेरिएबल में वास्तव में एक Fooवैल्यू है - कि सूचना कॉल चेन के नीचे प्रचारित है, और barकिसी भी चेक को करने की आवश्यकता नहीं है। क्योंकि Maybe Fooयह एक अलग प्रकार है Foo, आप इस संभावना को संभालने के लिए मजबूर हो सकते हैं कि इसमें शामिल हो सकते हैं Nothing, इसलिए आप कभी भी अंधा कर नहीं सकते NullPointerException। आप अपने कार्यक्रम के बारे में और अधिक आसानी से तर्क कर सकते हैं और संकलक यह जानकर अशक्त जांच छोड़ सकते हैं कि सभी प्रकार के चर Fooवास्तव में होते हैं Foo। हर कोई जीतता है।


पर्ल 6 में अशक्त मूल्यों के बारे में क्या? वे अभी भी अशक्त हैं, सिवाय इसके कि वे हमेशा टाइप किए जाते हैं। क्या यह अभी भी एक समस्या है जो आप लिख सकते हैं my Int $variable = Int, Intप्रकार का अशक्त मूल्य कहां है Int?
कोनराड बोरोवस्की

@ एक्सफ़िक्स मैं पर्ल से परिचित नहीं हूं, लेकिन जब तक आपके पास this type only contains integersविरोध करने के तरीके को लागू करने का एक तरीका नहीं है this type contains integers and this other value, आपको हर बार एक समस्या होगी जब आप केवल पूर्णांक चाहते हैं।
डोभाल

1
पैटर्न मिलान के लिए +1। उस विकल्प के प्रकारों के ऊपर दिए गए उत्तरों की संख्या के साथ, आपको लगता है कि किसी ने इस तथ्य का उल्लेख किया होगा कि पैटर्न मैचर्स की तरह एक सरल भाषा सुविधा उन्हें नल की तुलना में काम करने के लिए बहुत अधिक सहज बना सकती है।
जूल्स

1
मुझे लगता है कि पैटर्न मिलान की तुलना में मोनैडिक बाइंडिंग और भी अधिक उपयोगी है (हालांकि पैटर्न मिलान का उपयोग करके शायद बाइंड के लिए बाध्य किया जाता है)। मुझे लगता है कि यह एक तरह की तरह सी # मनोरंजक देखकर भाषाओं है बातें (के बहुत सारे के लिए में विशेष सिंटेक्स डाल ??, ?.चीजें हैं जो हास्केल जैसी भाषाओं के रूप में पुस्तकालय कार्यों को लागू करने के लिए, और इतने पर)। मुझे लगता है कि यह अधिक स्वच्छ है। null/ Nothingप्रचार किसी भी तरह से "विशेष" नहीं है, इसलिए हमें इसे करने के लिए अधिक वाक्यविन्यास सीखना चाहिए?
सारा

14

(पुराने सवाल के लिए मेरी टोपी को रिंग में फेंक देना;))

नल के साथ विशिष्ट समस्या यह है कि यह स्थिर टाइपिंग को तोड़ता है।

यदि मेरे पास एक है Thing t, तो संकलक गारंटी दे सकता है कि मैं कॉल कर सकता हूं t.doSomething()। खैर, UNLESS tरनटाइम में अशक्त है। अब सभी दांव बंद हैं। संकलक ने कहा कि यह ठीक है, लेकिन मुझे बहुत बाद में पता चला कि tयह वास्तव में नहीं है doSomething()। इसलिए टाइप त्रुटियों को पकड़ने के लिए संकलक पर भरोसा करने में सक्षम होने के बजाय, मुझे उन्हें पकड़ने के लिए रनटाइम तक इंतजार करना होगा। मैं भी सिर्फ पायथन का उपयोग कर सकता हूँ!

तो, एक तरह से, शून्य अनुमानित परिणामों के साथ एक गतिशील रूप से टाइप किए गए सिस्टम में गतिशील टाइपिंग का परिचय देता है।

उस अंतर को शून्य या नकारात्मक के लॉग से विभाजित करने, आदि के बीच का अंतर यह है कि जब मैं = 0, यह अभी भी एक इंट है। कंपाइलर अभी भी अपने प्रकार की गारंटी दे सकता है। समस्या यह है कि तर्क उस मान को गलत तरीके से लागू करता है जिसे तर्क द्वारा अनुमति नहीं है ... लेकिन यदि तर्क ऐसा करता है, तो यह बग की परिभाषा बहुत अधिक है।

(संकलक उन समस्याओं में से कुछ को पकड़ सकता है, BTW। i = 1 / 0संकलन समय पर जैसे झंडे के भाव । लेकिन आप वास्तव में संकलक से एक फ़ंक्शन का पालन करने और यह सुनिश्चित करने की उम्मीद नहीं कर सकते हैं कि पैरामीटर फ़ंक्शन के तर्क के अनुरूप हैं।)

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

स्ट्रिंग s = नया इंटेगर (1234);

तो इसे एक मान (अशक्त) को असाइनमेंट की अनुमति क्यों देनी चाहिए जो डी-संदर्भों को तोड़ देगा s?

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


3
सी में, प्रकार का एक चर *intया तो एक की पहचान करता है int, या NULL। इसी तरह C ++ में, एक प्रकार का एक चर *Widgetया तो एक की पहचान करता है Widget, एक ऐसी चीज जिसका प्रकार व्युत्पन्न होता है Widget, या NULL। जावा और डेरिवेटिव जैसे सी # के साथ समस्या यह है कि वे भंडारण स्थान Widgetका उपयोग करने के लिए करते हैं *Widget। इसका समाधान यह नहीं है कि यह दिखावा *Widgetनहीं करना चाहिए NULL, बल्कि एक उचित Widgetभंडारण-स्थान प्रकार रखने के लिए सक्षम नहीं होना चाहिए ।
सुपरकैट

14

एक nullसूचक एक उपकरण है

दुश्मन नहीं। तुम सिर्फ उन्हें सही उपयोग करना चाहिए।

एक सामान्य अमान्य पॉइंटर बग को खोजने और ठीक करने में कितना समय लगता है, इसके बारे में सिर्फ एक मिनट सोचें , एक न्यूल पॉइंटर बग को ढूंढने और ठीक करने की तुलना में। इसके खिलाफ पॉइंटर चेक करना आसान है null। यह सत्यापित करने के लिए एक पीटा है कि वैध डेटा के लिए एक गैर-शून्य सूचक इंगित करता है या नहीं।

यदि अभी भी आश्वस्त नहीं हैं, तो अपने परिदृश्य में मल्टीथ्रेडिंग की एक अच्छी खुराक जोड़ें, फिर सोचें।

मोरल ऑफ़ द स्टोरी: बच्चों को पानी के साथ मत फेंको। और दुर्घटना के लिए उपकरण को दोष न दें। बहुत समय पहले शून्य का आविष्कार करने वाला व्यक्ति काफी चतुर था, उस दिन के बाद से आप "कुछ भी नहीं" नाम दे सकते थे। nullसूचक अब तक उससे दूर नहीं है।


संपादित करें: हालांकि यह NullObjectपैटर्न उन संदर्भों की तुलना में बेहतर समाधान लगता है जो अशक्त हो सकते हैं, यह अपने आप ही समस्याओं का परिचय देता है:

  • एक NullObjectविधि (सिद्धांत के अनुसार) रखने वाला एक संदर्भ तब कुछ नहीं करता है जब एक विधि कहा जाता है। इस प्रकार, सूक्ष्म त्रुटियों को गलत तरीके से अनसाइन किए गए संदर्भों के कारण पेश किया जा सकता है, जो अब गैर-अशक्त (येहा!) होने की गारंटी है, लेकिन एक अवांछित कार्रवाई करते हैं: कुछ भी नहीं। NPE के साथ यह स्पष्ट है कि समस्या कहाँ है। इसके साथ NullObjectकि किसी भी तरह (लेकिन गलत) व्यवहार करता है, हम त्रुटियों का पता लगाने के जोखिम का परिचय देते हैं (बहुत) देर से। यह एक अमान्य पॉइंटर समस्या के समान दुर्घटना से नहीं है और इसके समान परिणाम हैं: संदर्भ कुछ को इंगित करता है, जो मान्य डेटा की तरह दिखता है, लेकिन डेटा त्रुटियों का परिचय नहीं देता है और इसे ट्रैक करने के लिए कठिन हो सकता है। सच कहूं, तो इन मामलों में मैं बिना पलक झपकाए एक एनपीई पसंद करता हूं जो तुरंत विफल हो जाता है, अब,एक तार्किक / डेटा त्रुटि पर जो अचानक मुझे बाद में सड़क के नीचे मारा। आईबीएम का अध्ययन याद रखें कि त्रुटियों की लागत के बारे में पता चलता है कि वे किस अवस्था में हैं?

  • जब एक विधि को कॉल NullObjectनहीं किया जाता है तो कुछ नहीं करने की धारणा , जब संपत्ति प्रापक या फ़ंक्शन को कॉल किया जाता है, या जब विधि के माध्यम से मान लौटता है out। मान लीजिए, वापसी मान एक है intऔर हम तय करते हैं, कि "कुछ नहीं करें" का अर्थ है "वापसी 0"। लेकिन हम कैसे सुनिश्चित हो सकते हैं, कि 0 सही "कुछ भी नहीं" है (वापस नहीं)? आखिरकार, NullObjectकुछ भी नहीं करना चाहिए, लेकिन जब एक मूल्य पूछा जाता है, तो उसे किसी भी तरह से प्रतिक्रिया करनी होगी। असफल होना एक विकल्प नहीं है: हम NullObjectएनपीई को रोकने के लिए उपयोग करते हैं , और हम निश्चित रूप से इसे किसी अन्य अपवाद के खिलाफ व्यापार नहीं करेंगे (लागू नहीं किया गया है, शून्य से विभाजित करें, ...), क्या हम? तो जब आप कुछ वापस करना चाहते हैं तो आप सही तरीके से कुछ भी नहीं लौटाते हैं।

मैं मदद नहीं कर सकता, लेकिन जब कोई NullObjectहर समस्या के पैटर्न को लागू करने की कोशिश करता है, तो यह एक और गलती करके एक गलती को सुधारने की कोशिश करता है। यह कुछ मामलों में बहुत अच्छा और उपयोगी समाधान है , लेकिन यह निश्चित रूप से सभी मामलों के लिए जादू की गोली नहीं है।


क्या आप एक तर्क पेश कर सकते हैं कि क्यों nullमौजूद होना चाहिए? किसी भी भाषा के दोष के बारे में सिर्फ "यह एक समस्या नहीं है, बस एक गलती मत करो" के बारे में हाथ करना संभव है।
डोभाल

किस मूल्य पर आप एक अमान्य सूचक सेट करेंगे?
जेन्सजी

ठीक है, आप उन्हें किसी भी मूल्य पर सेट नहीं करते हैं, क्योंकि आपको उन्हें बिल्कुल भी अनुमति नहीं देनी चाहिए। इसके बजाय, आप एक भिन्न प्रकार के एक चर का उपयोग करते हैं, शायद जिसे बुलाया Maybe Tजा सकता है या जिसमें Nothingकोई Tमूल्य हो। यहाँ महत्वपूर्ण बात यह है कि यदि आप इसका उपयोग करने की अनुमति देने से पहले Maybeवास्तव में इसमें शामिल हैं या नहीं, तो यह जाँचने के लिए मजबूर हैं कि आप Tऐसा नहीं करते हैं। और क्योंकि आपने अमान्य पॉइंटर्स को अस्वीकार कर दिया है, अब आपको कभी भी कुछ भी चेक नहीं करना है जो कि नहीं है Maybe
डोभाल

1
@JensG अपने नवीनतम उदाहरण में, क्या कंपाइलर आपको हर कॉल करने से पहले अशक्त जांच करता है t.Something()? या क्या आपके पास यह जानने का कोई तरीका है कि आपने पहले से ही चेक किया था, और आपको दोबारा ऐसा नहीं करना चाहिए? यदि आपने tइस विधि में पास होने से पहले चेक किया था तो क्या होगा ? विकल्प के प्रकार के साथ, कंपाइलर जानता है कि आपने अभी तक चेक किया है या नहीं के प्रकार को देखकर t। यदि यह एक विकल्प है, तो आपने इसे चेक नहीं किया है, जबकि यदि यह अपरिवर्तित मूल्य है तो आपके पास है।
टिम गुडमैन

1
जब आप मूल्य के लिए कहते हैं, तो आप क्या अशक्त वस्तु लौटाते हैं?
जेन्सजी

8

अशक्त संदर्भ एक गलती है क्योंकि वे गैर-संवेदी कोड की अनुमति देते हैं:

foo = null
foo.bar()

यदि आप प्रकार प्रणाली का लाभ उठाते हैं, तो विकल्प हैं:

Maybe<Foo> foo = null
foo.bar() // error{Maybe<Foo> does not have any bar method}

आम तौर पर विचार चर को एक बॉक्स में रखना है, और केवल एक चीज जो आप कर सकते हैं, वह इसे अनबॉक्स करना है, अधिमानतः एफिल के लिए प्रस्तावित जैसे कंपाइलर सहायता को सूचीबद्ध करना।

हास्केल ने इसे खरोंच से ( Maybe), सी ++ में आप लाभ उठा सकते हैं boost::optional<T>लेकिन आप अभी भी अपरिभाषित व्यवहार प्राप्त कर सकते हैं ...


6
"लीवरेज" के लिए -1!
मार्क सी

8
लेकिन निश्चित रूप से बहुत सारे सामान्य प्रोग्रामिंग निर्माण निरर्थक कोड के लिए अनुमति देते हैं, नहीं? शून्य से विभाजन (यकीनन) निरर्थक है, लेकिन हम अभी भी विभाजन की अनुमति देते हैं, और उम्मीद है कि प्रोग्रामर को यह याद रखना है कि विभाजक गैर-शून्य है (या अपवाद को संभालना)।
टिम गुडमैन

3
मुझे विश्वास है कि आप "शून्य से" से क्या मतलब है नहीं कर रहा हूँ, लेकिन Maybeकोर भाषा में नहीं बनाया गया है, इसकी परिभाषा सिर्फ मानक प्रस्तावना में होना होता है: data Maybe t = Nothing | Just t
fredoverflow

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

2
@TimGoodman जबकि शून्य से विभाजन सभी संख्यात्मक मान प्रकारों के लिए सबसे अच्छा उदाहरण नहीं हो सकता है (IEEE 754 शून्य से विभाजन के लिए एक परिणाम को परिभाषित करता है), ऐसे मामलों में जहां यह अपवाद को फेंक देगा, बजाय टाइप के Tअन्य मूल्यों द्वारा विभाजित प्रकार के मूल्यों को रखने के बजाय। T, आप प्रकार के मूल्यों से Tविभाजित प्रकार के मूल्यों के लिए ऑपरेशन को सीमित करेंगे NonZero<T>
kbolino

8

और विकल्प क्या है?

वैकल्पिक प्रकार और पैटर्न मिलान। चूँकि मैं C # नहीं जानता, इसलिए यहाँ एक काल्पनिक भाषा में कोड का एक टुकड़ा है, जिसे Scala # :-) कहा जाता है।

Customer.GetByLastName("Goodman")    // returns Option[Customer]
match
{
    case Some(customer) =>
    Console.WriteLine(customer.FirstName + " " + customer.LastName + " is awesome!");

    case None =>
    Console.WriteLine("There was no customer named Goodman.  How lame!");
}

6

नल के साथ समस्या यह है कि ऐसी भाषाएं जो आपको इसके खिलाफ रक्षात्मक रूप से प्रोग्रामिंग में बहुत अधिक बल देती हैं। यह सुनिश्चित करने के लिए बहुत अधिक प्रयास (रक्षात्मक अगर-ब्लॉक का उपयोग करने की कोशिश करने की तुलना में अधिक) होता है

  1. वस्तुओं आप उन्हें उम्मीद नहीं अशक्त होने के लिए वास्तव में कभी नहीं अशक्त हैं, और
  2. कि आपके रक्षात्मक तंत्र वास्तव में सभी संभावित एनपीई से प्रभावी ढंग से निपटते हैं।

तो, वास्तव में, nulls अंत में एक महंगी चीज है।


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

2
आह, मैंने खुद को स्पष्ट रूप से नहीं समझाया। ऐसा नहीं है कि नल से निपटना अधिक कठिन है। यह है कि यह अत्यंत कठिन है (यदि असंभव नहीं है) यह गारंटी देना कि संभावित रूप से अशक्त संदर्भों तक सभी पहुंच पहले से ही सुरक्षित है। यही है, एक ऐसी भाषा में जो अशक्त संदर्भों की अनुमति देती है, यह गारंटी देना असंभव है कि मनमाने आकार के कोड का एक टुकड़ा अशक्त सूचक अपवादों से मुक्त है, कुछ बड़ी कठिनाइयों और प्रयासों के बिना नहीं। यही कारण है कि अशक्त संदर्भ एक महंगी चीज है। अशक्त सूचक अपवादों की पूर्ण अनुपस्थिति की गारंटी देना बेहद महंगा है।
luis.espinal

4

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


4

समस्या इतनी ज्यादा अशक्त नहीं है, यह है कि आप बहुत सारे आधुनिक भाषाओं में गैर-अशक्त संदर्भ प्रकार निर्दिष्ट नहीं कर सकते।

उदाहरण के लिए, आपका कोड लग सकता है

public void MakeCake(Egg egg, Flour flour, Milk milk)
{
    if (egg == null) { throw ... }
    if (flour == null) { throw ... }
    if (milk == null) { throw ... }

    egg.Crack();
    MixingBowl.Mix(egg, flour, milk);
    // etc
}

// inside Mixing bowl class
public void Mix(Egg egg, Flour flour, Milk milk)
{
    if (egg == null) { throw ... }
    if (flour == null) { throw ... }
    if (milk == null) { throw ... }

    //... etc
}

जब कक्षा के संदर्भ पास हो जाते हैं, तो रक्षात्मक प्रोग्रामिंग आपको अशक्त के लिए सभी मापदंडों को फिर से जांचने के लिए प्रोत्साहित करती है, भले ही आपने उन्हें पहले से ही सही के लिए चेक किया हो, खासकर जब परीक्षण के तहत पुन: प्रयोज्य इकाइयों का निर्माण करते हैं। उसी संदर्भ को कोडबेस के 10 बार के लिए आसानी से जांचा जा सकता है!

क्या यह बेहतर नहीं होगा यदि आपके पास एक सामान्य अशक्त प्रकार हो सकता है जब आप इस चीज़ को प्राप्त करते हैं, तब और उसके साथ अशक्त व्यवहार करते हैं, फिर इसे अपने सभी छोटे सहायक कार्यों और कक्षाओं के लिए एक अशक्त प्रकार के रूप में पास करें, बिना सभी की जाँच के समय?

विकल्प समाधान के बिंदु - आपको त्रुटि राज्यों पर स्विच करने की अनुमति नहीं है, लेकिन उन कार्यों के डिजाइन की अनुमति देने के लिए जो स्पष्ट रूप से अशक्त तर्क स्वीकार नहीं कर सकते हैं। प्रकारों के "डिफ़ॉल्ट" क्रियान्वयन अशक्त हैं या गैर-अशक्त दोनों उपकरण उपलब्ध होने के लचीलेपन से कम महत्वपूर्ण नहीं है।

http://twistedoakstudios.com/blog/Post330_non-nullable-types-vs-c-fixing-the-billion-dollar-mistake

यह C # -> https://visualstudio.uservoice.com/forums/121579-visual-studio/category/30931-languages-c के लिए # 2 सबसे अधिक वोट वाले फ़ीचर के रूप में भी सूचीबद्ध है।


मुझे लगता है कि विकल्प <टी> के बारे में एक और महत्वपूर्ण बात यह है कि यह एक सन्यासी है (और इसलिए एक एंडोफ़नक्टर भी है), इसलिए आप मूल्य धाराओं पर जटिल संगणनाओं को लिख सकते हैं, जिसमें वैकल्पिक तरीके से बिना किसी प्रकार के जाँच किए Noneहुए हैं।
सारा

3

अशक्त बुराई है। हालांकि, एक अशक्त की कमी एक बड़ी बुराई हो सकती है।

समस्या यह है कि वास्तविक दुनिया में आपके पास अक्सर ऐसी स्थिति होती है जहां आपके पास डेटा नहीं होता है। नल के बिना संस्करण का आपका उदाहरण अभी भी उड़ा सकता है - या तो आपने एक तर्क गलती की और गुडमैन के लिए जांच करना भूल गया या शायद गुडमैन ने जब आप की जाँच की और जब आपने उसे देखा तो आपस में शादी कर ली। (यह तर्क का मूल्यांकन करने में मदद करता है यदि आप आंकड़ा करते हैं कि मोरियार्टी आपके हर कोड को देख रहा है और आपको यात्रा करने की कोशिश कर रहा है।)

गुडमैन की तलाश तब होती है जब वह वहां नहीं होता है। एक अशक्त के बिना आपको किसी प्रकार के डिफ़ॉल्ट ग्राहक को वापस करना होगा - और अब आप उस डिफ़ॉल्ट को सामान बेच रहे हैं।

मूल रूप से, यह नीचे आता है कि क्या यह अधिक महत्वपूर्ण है कि कोड काम करता है या नहीं, यह सही ढंग से काम करता है। यदि अनुचित व्यवहार किसी व्यवहार के लिए बेहतर है, तो आप अशक्त नहीं चाहते हैं। (यह सही विकल्प कैसे हो सकता है, इसके एक उदाहरण के लिए, एरियन वी के पहले लॉन्च पर विचार करें। एक अनिर्धारित / 0 त्रुटि के कारण रॉकेट को एक कठिन मोड़ देना पड़ा और इसके कारण अलग हो जाने पर स्व-विनाश हो गया। यह गणना करने की कोशिश कर रहा था कि वास्तव में किसी भी उद्देश्य से तत्काल किसी भी उद्देश्य से सेवा नहीं दी गई थी - यह नियमित वापसी के बावजूद कक्षा बना देता था।)

100 में से कम से कम 99 बार मैं नल का उपयोग करना चुनूंगा। मैं खुशी के लिए उनके स्वयं के संस्करण पर ध्यान देना चाहूंगा, हालांकि।


1
यह एक अच्छा जवाब है। प्रोग्रामिंग में अशक्त निर्माण समस्या नहीं है, यह सभी शून्य मानों के उदाहरण हैं। यदि आप एक डिफ़ॉल्ट ऑब्जेक्ट बनाते हैं, तो अंततः आपके सभी नल उन डिफॉल्टों से बदल दिए जाएंगे और आपके UI, DB और हर जगह बीच में उन लोगों से भर जाएंगे, जो शायद अधिक उपयोगी नहीं हैं। लेकिन आप रात को सो जाएँगे क्योंकि कम से कम आपका कार्यक्रम दुर्घटनाग्रस्त तो नहीं है।
क्रिस मैक्कल

2
आप मान लेते हैं कि nullमूल्य के अभाव में मॉडलिंग का एकमात्र (या यहां तक ​​कि बेहतर) तरीका है।
सारा

1

सबसे सामान्य मामले के लिए ऑप्टिमाइज़ करें।

हर समय अशक्त रहने के लिए जाँच करना - आप ग्राहक वस्तु को पकड़ कर उसके साथ काम करना चाहते हैं।

सामान्य स्थिति में, यह ठीक काम करना चाहिए - आप ऊपर देखते हैं, ऑब्जेक्ट प्राप्त करते हैं और इसका उपयोग करते हैं।

में असाधारण मामला है, जहां आप (अनियमित) नाम से एक ग्राहक को देख रहे हैं, नहीं जानते हुए भी कि क्या है कि रिकॉर्ड / वस्तु मौजूद है, तो आप कुछ संकेत है कि इस में विफल रहा है देख सकते हैं। इस स्थिति में, उत्तर RecordNotFound अपवाद को फेंकने के लिए है (या SQL प्रदाता को आपके लिए ऐसा करने दें)।

यदि आप ऐसी स्थिति में हैं, जहां आप नहीं जानते कि आप (पैरामीटर) में आने वाले डेटा पर भरोसा कर सकते हैं, शायद इसलिए कि यह किसी उपयोगकर्ता द्वारा दर्ज किया गया था, तो आप 'TryGetCustomer (नाम, ग्राहक से बाहर) भी प्रदान कर सकते हैं' पैटर्न। Int.Parse और int.TryParse के साथ क्रॉस-रेफरेंस।


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

0

अपवाद स्पष्ट नहीं हैं!

वे वहां किसी कारण की वजह से हैं। यदि आपका कोड खराब चल रहा है, तो एक जीनियस पैटर्न है, जिसे अपवाद कहा जाता है , और यह आपको बताता है कि कोई चीज गलत है।

अशक्त वस्तुओं के उपयोग से बचने से आप उन अपवादों का हिस्सा छिपा रहे हैं। मैं ओपी उदाहरण के बारे में नहीं बोल रहा हूं, जहां वह अशक्त सूचक अपवाद को अच्छी तरह से अपवाद में बदल देता है, यह वास्तव में अच्छी बात हो सकती है क्योंकि यह पठनीयता है। जब आप विकल्प प्रकार का समाधान लेते हैं, जैसा कि @ जोंस ने बताया है, तो आप अपवाद छिपा रहे हैं।

जब आप खाली विकल्प चुनने के लिए बटन पर क्लिक कर रहे हों, तो अपवाद के बजाय, अपने उत्पादन अनुप्रयोग में, कुछ भी नहीं हो रहा है। इसके बजाय अशक्त सूचक अपवाद को फेंक दिया जाएगा और हमें संभवतः उस अपवाद की रिपोर्ट मिल जाएगी (जैसा कि कई उत्पादन अनुप्रयोगों में हमारे पास ऐसा तंत्र है), और हम वास्तव में इसे ठीक कर सकते हैं।

अपवाद को टालकर अपने कोड को बुलेटप्रूफ बनाना बुरा विचार है, अपवाद को ठीक करके आपको कोड बुलेटप्रूफ बना देता है, अच्छी तरह से यह वह रास्ता है जिसे मैं चुनूंगा।


1
मैं विकल्प प्रकार के बारे में अब कुछ अधिक जानता हूं, जब मैंने कुछ साल पहले प्रश्न पोस्ट किया था, क्योंकि अब मैं नियमित रूप से स्काला में उनका उपयोग करता हूं। विकल्प का मुद्दा यह है कि यह Noneकिसी ऐसी चीज को असाइन करता है जो संकलन-समय की त्रुटि में विकल्प नहीं है, और इसी तरह से इसका उपयोग करते हुए Optionयदि इसका मूल्य पहले जाँच के बिना है तो यह संकलन-समय त्रुटि है। संकलन त्रुटियों निश्चित रूप से रन-टाइम अपवादों की तुलना में अच्छे हैं।
टिम गुडमैन

उदाहरण के लिए: आप नहीं कह सकते s: String = None, आपको कहना होगा s: Option[String] = None। और अगर sकोई विकल्प है, तो आप यह नहीं कह सकते हैं s.length, हालांकि आप यह जांच सकते हैं कि इसका एक मूल्य है और एक ही समय में मूल्य निकाल सकते हैं, पैटर्न मिलान के साथ:s match { case Some(str) => str.length; case None => throw RuntimeException("Where's my value?") }
टिम गुडमैन

दुर्भाग्य से स्काला में आप अभी भी कह सकते हैं s: String = null, लेकिन यह जावा के साथ अंतर की कीमत है। हम आम तौर पर अपने स्काला कोड में उस तरह की चीज को अस्वीकार कर देते हैं।
टिम गुडमैन

2
हालाँकि मैं इस सामान्य टिप्पणी से सहमत हूँ कि अपवाद (सही तरीके से प्रयुक्त) कोई बुरी बात नहीं है, और इसे "निगल" कर एक अपवाद को ठीक करना आमतौर पर एक भयानक विचार है। लेकिन विकल्प प्रकार के साथ, लक्ष्य अपवादों को संकलन-समय त्रुटियों में बदलना है, जो एक बेहतर चीज है।
टिम गुडमैन

@TimGoodman यह केवल रणनीति है या इसे जावा और एक्शनस्क्रिप्ट 3 जैसी अन्य भाषाओं में इस्तेमाल किया जा सकता है? साथ ही यह अच्छा होगा कि आप अपने उत्तर को पूरे उदाहरण के साथ संपादित कर सकें।
इल्या गज़मैन

0

Nullsसमस्याग्रस्त हैं क्योंकि उन्हें स्पष्ट रूप से जांचना चाहिए, फिर भी संकलक आपको चेतावनी देने में असमर्थ हैं कि आप उनके लिए जांच करना भूल गए हैं। केवल समय लेने वाली स्थैतिक विश्लेषण आपको बता सकता है कि। सौभाग्य से, कई अच्छे विकल्प हैं।

चर को दायरे से बाहर निकालें। जिस तरह से अक्सर nullएक प्रोग्राम धारक के रूप में प्रयोग किया जाता है जब एक प्रोग्रामर एक चर को बहुत जल्दी घोषित करता है या उसे बहुत लंबा रखता है। सबसे अच्छा तरीका बस चर से छुटकारा पाने के लिए है। इसे तब तक घोषित न करें जब तक कि आपके पास वहाँ रखने के लिए वैध मूल्य न हो। यह उतना कठिन नहीं है जितना कि आप सोच सकते हैं, और यह आपके कोड को बहुत साफ करता है।

नल ऑब्जेक्ट पैटर्न का उपयोग करें। कभी-कभी, एक लापता मान आपके सिस्टम के लिए एक मान्य स्थिति है। नल ऑब्जेक्ट पैटर्न यहां एक अच्छा समाधान है। यह लगातार स्पष्ट जांच की आवश्यकता से बचा जाता है, लेकिन फिर भी आपको एक अशक्त स्थिति का प्रतिनिधित्व करने की अनुमति देता है। जैसा कि कुछ लोग दावा करते हैं, यह "एक त्रुटि स्थिति पर पैपिंग" नहीं है, क्योंकि इस मामले में एक अशक्त राज्य एक वैध अर्थ राज्य है। इसका अर्थ है कि आप इस पैटर्न का उपयोग नहीं करना चाहिए जब एक अशक्त राज्य नहीं है एक वैध अर्थ राज्य। आपको बस अपने वेरिएबल को दायरे से बाहर ले जाना चाहिए।

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

val customer = customerDb getByLastName "Goodman"
val awesomeMessage =
  customer map (c => s"${c.firstName} ${c.lastName} is awesome!")
val notFoundMessage = "There was no customer named Goodman.  How lame!"
println(awesomeMessage getOrElse notFoundMessage)

दूसरी पंक्ति में, जहाँ हम भयानक संदेश का निर्माण कर रहे हैं, हम ग्राहक नहीं पाए जाने पर एक स्पष्ट जाँच नहीं करते हैं और वह सुंदरता है जिसकी हमें आवश्यकता नहीं है । यह बहुत अंतिम पंक्ति तक नहीं है, जो बाद में कई लाइनें हो सकती है, या यहां तक ​​कि एक अलग मॉड्यूल में भी, जहां हम स्पष्ट रूप से खुद को चिंता करते हैं कि क्या होता Optionहै None। इतना ही नहीं, अगर हम इसे करना भूल गए थे, तो यह टाइप किया हुआ नहीं होता। फिर भी, यह एक स्पष्ट ifविवरण के बिना, एक बहुत ही स्वाभाविक तरीके से किया जाता है ।

इसके विपरीत null, जहां यह टाइप करता है, वह ठीक प्रकार से चेक करता है, लेकिन जहां आपको रास्ते के हर चरण पर एक स्पष्ट जांच करनी होती है या आपका पूरा एप्लिकेशन रनटाइम में उड़ा देता है, यदि आप उपयोग के मामले में भाग्यशाली हैं तो आपकी यूनिट परीक्षण अभ्यास करती है। यह सिर्फ परेशानी के लायक नहीं है।


0

NULL की केंद्रीय समस्या यह है कि यह सिस्टम को अविश्वसनीय बनाता है। 1980 में टोनी होरे ने अपने ट्यूरिंग अवार्ड के लिए समर्पित पत्र में लिखा:

और इसलिए, एडीए के मूल और डिजाइनरों के लिए मेरी सलाह को नजरअंदाज कर दिया गया है। ...। इस भाषा को अपने वर्तमान स्थिति में उन अनुप्रयोगों में उपयोग करने की अनुमति न दें, जहां विश्वसनीयता महत्वपूर्ण है, अर्थात, परमाणु ऊर्जा स्टेशन, क्रूज मिसाइल, प्रारंभिक चेतावनी प्रणाली, एंटीबॉडी मिसाइल रक्षा प्रणाली। प्रोग्रामिंग भाषा की त्रुटि के परिणामस्वरूप रास्ता भटकने वाला अगला रॉकेट हो सकता है कि शुक्र की एक हानिरहित यात्रा पर एक खोजी अंतरिक्ष रॉकेट न हो: यह हमारे अपने शहरों में से एक पर एक परमाणु बम विस्फोट हो सकता है। एक अविश्वसनीय प्रोग्रामिंग भाषा, जो अविश्वसनीय कार्यक्रम उत्पन्न करती है, हमारे पर्यावरण और हमारे समाज के लिए असुरक्षित कारों, विषाक्त कीटनाशकों, या परमाणु ऊर्जा स्टेशनों पर दुर्घटनाओं की तुलना में कहीं अधिक जोखिम पैदा करती है। जोखिम को कम करने के लिए सतर्क रहें, इसे बढ़ाने के लिए नहीं।

उसके बाद से ADA भाषा बहुत बदल गई है, लेकिन ऐसी समस्याएं अभी भी जावा, C # और कई अन्य लोकप्रिय भाषाओं में मौजूद हैं।

ग्राहक और आपूर्तिकर्ता के बीच अनुबंध बनाना डेवलपर का कर्तव्य है। उदाहरण के लिए, C # में, जैसा कि जावा में है, आप आसानी Genericsसे (दो विकल्प) Nullबनाकर संदर्भ के प्रभाव को कम करने के लिए उपयोग कर सकते हैं NullableClass<T>:

class NullableClass<T>
{
     public HasValue {get;}
     public T Value {get;}
}

और फिर इसका उपयोग करें

NullableClass<Customer> customer = dbRepository.GetCustomer('Mr. Smith');
if(customer.HasValue){
  // one logic with customer.Value
}else{
  // another logic
} 

या C # एक्सटेंशन विधियों के साथ दो विकल्प शैली का उपयोग करें:

customer.Do(
      // code with normal behaviour
      ,
      // what to do in case of null
) 

अंतर महत्वपूर्ण है। एक विधि के एक ग्राहक के रूप में आप जानते हैं कि क्या उम्मीद है। एक टीम का नियम हो सकता है:

यदि एक वर्ग प्रकार NullableClass का नहीं है, तो यह उदाहरण अशक्त नहीं होना चाहिए

टीम अनुबंध द्वारा डिजाइन और संकलित समय पर स्थैतिक जाँच के द्वारा इस विचार को मजबूत कर सकती है , उदाहरण के लिए पूर्व शर्त के साथ:

function SaveCustomer([NotNullAttribute]Customer customer){
     // there is no need to check whether customer is null 
     // it is a client problem, not this supplier
}

या एक स्ट्रिंग के लिए

function GetCustomer([NotNullAndNotEmptyAttribute]String customerName){
     // there is no need to check whether customerName is null or empty 
     // it is a client problem, not this supplier
}

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


0

नहीं।

एक भाषा का विशेष मूल्य की तरह असफल होना nullभाषा की समस्या है। यदि आप कोटलिन या स्विफ्ट जैसी भाषाओं को देखते हैं , जो लेखक को हर मुठभेड़ में एक अशक्त मूल्य की संभावना से स्पष्ट रूप से निपटने के लिए मजबूर करती हैं, तो कोई खतरा नहीं है।

सवाल में एक जैसी भाषाओं में, भाषा की अनुमति देता है nullकिसी भी एक मूल्य होने की -ish , प्रकार, लेकिन वह बाद में स्वीकार करते हैं, जो बातें की जा रही है की ओर जाता है के लिए किसी भी निर्माणों प्रदान नहीं करता है nullअप्रत्याशित रूप से (खासकर जब कहीं और से आप के लिए पारित किया है), और इस प्रकार आप दुर्घटनाग्रस्त हो जाते हैं। यह बुराई है: nullआपको इससे निपटने के बिना उपयोग करने देना।


-1

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

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


-1

मुझे एक साधारण आपत्ति है null:

यह अस्पष्टता का परिचय देकर आपके कोड के शब्दार्थ को पूरी तरह से तोड़ देता है

अक्सर आप उम्मीद करते हैं कि परिणाम एक निश्चित प्रकार का होगा। यह शब्दार्थ है। कहते हैं, आपने एक निश्चित के साथ उपयोगकर्ता के लिए डेटाबेस पूछा था id, आप एक निश्चित प्रकार (= user) के होने की उम्मीद करते हैं । लेकिन, अगर उस आईडी का कोई उपयोगकर्ता नहीं है, तो क्या होगा? एक (बुरा) समाधान है: एक nullमूल्य जोड़ना । तो परिणाम अस्पष्ट है : या तो यह है userया यह है null। लेकिन nullअपेक्षित प्रकार नहीं है। और यहीं से कोड की महक शुरू होती है।

बचने में null, आप अपना कोड शब्दार्थ से स्पष्ट करते हैं।

हमेशा आस-पास के तरीके होते हैं null: संग्रह को फिर से भरना Null-objects, optionalsआदि।


-2

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

  1. स्थानों को एक शून्य सूचक के लिए डिफ़ॉल्ट है, लेकिन दुर्घटना के बिना कोड को उन्हें देखने की अनुमति न दें (या यह भी निर्धारित करें कि वे अशक्त हैं)। संभवतः अशक्त करने के लिए पॉइंटर सेट करने का एक स्पष्ट साधन प्रदान करें।
  2. यदि कोई रीड करने का प्रयास करता है, तो स्थानों को एक शून्य पॉइंटर के लिए डिफ़ॉल्ट किया जाता है, और क्रैश किया जाता है, लेकिन एक पॉइंटर को अशक्त रखने के परीक्षण के लिए एक गैर-क्रैशिंग साधन प्रदान करता है। एक संकेतक को शून्य करने के लिए एक खोज साधन भी प्रदान करें।
  3. संकेतित प्रकार के कुछ विशेष संकलक-आपूर्ति की गई डिफ़ॉल्ट आवृत्ति के लिए सूचक के लिए स्थान डिफ़ॉल्ट हैं।
  4. संकेत के प्रकार के लिए कुछ विशेष प्रोग्राम-प्रदत्त डिफ़ॉल्ट आवृत्ति के लिए सूचक के लिए स्थान डिफ़ॉल्ट हैं।
  5. किसी भी अशक्त-पॉइंटर का उपयोग करें (न कि केवल डेरेफेरिंग संचालन) एक उदाहरण की आपूर्ति के लिए कुछ प्रोग्राम-आपूर्ति की गई दिनचर्या को कॉल करें।
  6. भाषा के शब्दार्थों को ऐसे डिज़ाइन करें कि संग्रहण स्थानों का संग्रह मौजूद नहीं होगा, एक दुर्गम संकलक को छोड़कर, जब तक कि सभी सदस्यों पर आरंभीकरण रूटीन चलाए गए हों (किसी सरणी निर्माता की तरह कुछ या तो डिफ़ॉल्ट उदाहरण के साथ आपूर्ति की जाएगी) एक फ़ंक्शन जो किसी इंस्टेंस को लौटाएगा, या संभवतः फ़ंक्शन की एक जोड़ी - एक इंस्टेंस को वापस करने के लिए और एक जिसे पूर्व-निर्मित इंस्टेंस पर बुलाया जाएगा यदि सरणी के निर्माण के दौरान कोई अपवाद होता है)।

अंतिम विकल्प में कुछ अपील हो सकती है, खासकर अगर एक भाषा में अशक्त और गैर-अशक्त दोनों प्रकार के होते हैं (कोई विशेष प्रकार के निर्माणकर्ता को किसी भी प्रकार के लिए कॉल कर सकता है, लेकिन गैर-अशक्त प्रकारों के सरणियों को बनाते समय केवल उन्हें कॉल करने की आवश्यकता होती है) लेकिन संभवत: उस समय के आसपास संभव नहीं था जब अशक्त बिंदुओं का आविष्कार किया गया था। अन्य विकल्पों में से, शून्य बिंदुओं को कॉपी करने की अनुमति देने की तुलना में कोई भी अधिक आकर्षक नहीं लगता है, लेकिन डेरेफ़र्ड या अनुक्रमित नहीं है। दृष्टिकोण # 4 एक विकल्प के रूप में सुविधाजनक हो सकता है, और इसे लागू करने के लिए काफी सस्ता होना चाहिए, लेकिन यह निश्चित रूप से एकमात्र विकल्प नहीं होना चाहिए। किसी विशेष मान्य ऑब्जेक्ट के लिए पॉइंटर्स को डिफ़ॉल्ट रूप से इंगित करना आवश्यक है, पॉइंटर्स को एक शून्य मान के लिए डिफ़ॉल्ट होने की तुलना में बहुत खराब है जिसे पढ़ा या कॉपी किया जा सकता है लेकिन डीरएफ़र्ड या इंडेक्स नहीं किया गया है।


-2

हां, ऑब्जेक्ट-ओरिएंटेड दुनिया में NULL एक भयानक डिजाइन है। संक्षेप में, NULL उपयोग की ओर जाता है:

  • तदर्थ त्रुटि से निपटने (अपवादों के बजाय)
  • अस्पष्ट शब्दार्थ
  • तेजी से असफल होने के बजाय धीमा
  • कंप्यूटर सोच बनाम वस्तु सोच
  • उत्परिवर्तनीय और अपूर्ण वस्तुएं

विस्तृत विवरण के लिए इस ब्लॉग पोस्ट को देखें : http://www.yegor256.com/2014/05/13/why-null-is-bad.html

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