C # 8 गैर-अशक्त संदर्भ और कोशिश पैटर्न


23

सी # वर्गों में एक पैटर्न है, जिसके द्वारा अनुकरण किया गया है Dictionary.TryGetValueऔर int.TryParse: एक विधि जो एक ऑपरेशन की सफलता का संकेत देती है और वास्तविक परिणाम वाले एक आउट पैरामीटर है; यदि ऑपरेशन विफल हो जाता है, तो आउट पैरामीटर को शून्य पर सेट किया जाता है।

मान लेते हैं कि मैं C # 8 गैर-अशक्त संदर्भों का उपयोग कर रहा हूं और अपने स्वयं के वर्ग के लिए एक TryParse विधि लिखना चाहता हूं। सही हस्ताक्षर यह है:

public static bool TryParse(string s, out MyClass? result);

क्योंकि परिणाम झूठे मामले में अशक्त है, आउट चर को अशक्त के रूप में चिह्नित किया जाना चाहिए।

हालाँकि, ट्राइ पैटर्न का उपयोग आमतौर पर इस तरह किया जाता है:

if (MyClass.TryParse(s, out var result))
{
  // use result here
}

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

if (MyClass.TryParse(s, out var result))
{
  Console.WriteLine("Look: {0}", result.SomeProperty); // compiler warning, could be null
  Console.WriteLine("Look: {0}", result!.SomeProperty); // need override
}

यह बदसूरत है और थोड़ा गैर-आर्थिक है।

विशिष्ट उपयोग पैटर्न के कारण, मेरे पास एक और विकल्प है: परिणाम प्रकार के बारे में झूठ:

public static bool TryParse(string s, out MyClass result) // not nullable
{
   // Happy path sets result to non-null and returns true.
   // Error path does this:
   result = null!; // override compiler complaint
   return false;
}

अब विशिष्ट उपयोग अच्छे हो जाते हैं:

if (MyClass.TryParse(s, out var result))
{
  Console.WriteLine("Look: {0}", result.SomeProperty); // no warning
}

लेकिन असामान्य उपयोग से यह चेतावनी नहीं मिलनी चाहिए:

else
{
  Console.WriteLine("Fail: {0}", result.SomeProperty);
  // Yes, result is in scope here. No, it will never be non-null.
  // Yes, it will throw. No, the compiler won't warn about it.
}

अब मुझे यकीन नहीं है कि यहां कौन सा रास्ता जाना है। क्या सी # भाषा टीम की आधिकारिक सिफारिश है? क्या कोई CoreFX कोड पहले से ही गैर-अशांत संदर्भों में परिवर्तित हो गया है जो मुझे दिखा सकता है कि यह कैसे करना है? (मैं TryParseतरीकों की तलाश में गया था । IPAddressएक वर्ग है जिसमें एक है, लेकिन इसे कोरफैक्स की मास्टर शाखा में परिवर्तित नहीं किया गया है)

और इससे Dictionary.TryGetValueनिपटने के लिए सामान्य कोड कैसे होता है ? (संभवतः एक विशेष MaybeNullविशेषता के साथ जो मैंने पाया है।) क्या होता है जब मैं Dictionaryएक गैर-अशक्त मान प्रकार के साथ त्वरित करता हूं ?


यह कोशिश नहीं की (यही कारण है कि मैं इसे एक उत्तर के रूप में नहीं लिख रहा हूं), लेकिन स्विच स्टेटमेंट के नए पैटर्न मिलान सुविधा के साथ, मैं एक विकल्प का अनुमान लगा रहा हूं कि बस अशक्त संदर्भ (कोई कोशिश-पैटर्न नहीं लौटाएं) वापसी MyClass?), और एक case MyClass myObj:(एक विकल्प) के साथ उस पर एक स्विच करें case null:
फिलिप मिलोवानोविक

+1 मुझे वास्तव में यह सवाल पसंद है, और जब मुझे इसके साथ काम करना होता है, तो मैंने हमेशा ओवरराइड के बजाय केवल एक अतिरिक्त अशक्त जांच का उपयोग किया है - जो हमेशा थोड़ा अनावश्यक और असमान महसूस करता था, लेकिन यह प्रदर्शन में कभी भी महत्वपूर्ण कोड नहीं था इसलिए मैंने अभी इसे जाने दिया। यह देखने के लिए अच्छा होगा कि क्या इसे संभालने का एक क्लीनर तरीका है!
ब्रायनएच

जवाबों:


10

जैसा कि आप वर्णन करते हैं कि बूल / आउट-वैर पैटर्न अशक्त संदर्भ प्रकारों के साथ अच्छी तरह से काम नहीं करता है। इसलिए संकलक से लड़ने के बजाय, चीजों को सरल बनाने के लिए सुविधा का उपयोग करें। C # 8 के उन्नत पैटर्न मिलान सुविधाओं में फेंकें और आप एक अशक्त संदर्भ को "गरीब आदमी की शायद प्रकार" के रूप में मान सकते हैं:

public static MyClass? TryParse(string s) => 



if (TryParse(someString) is {} myClass)
{
    // myClass wasn't null, we are good to use it
}

इस तरह, आप outमापदंडों के साथ खिलवाड़ करने से बचते हैं और आपको nullगैर-अशक्त संदर्भों के साथ मिश्रण करने के लिए कंपाइलर के साथ संघर्ष नहीं करना पड़ता है ।

और इससे Dictionary.TryGetValueनिपटने के लिए सामान्य कोड कैसे होता है ?

इस बिंदु पर, कि "गरीब आदमी का प्रकार" नीचे गिर सकता है। आपके सामने चुनौती यह होगी कि अशक्त संदर्भ प्रकारों (NRT) का उपयोग करते समय, संकलक Foo<T>गैर-अशक्त माना जाएगा । लेकिन कोशिश करो और इसे बदलो Foo<T?>और यह चाहते हैं कि Tएक वर्ग या संरचना को विवश किया जाए क्योंकि अशक्त मूल्य प्रकार सीएलआर के दृष्टिकोण से बहुत अलग चीज हैं। इसके लिए कई तरह के काम होते हैं:

  1. NRT सुविधा को सक्षम न करें,
  2. मापदंडों के लिए default(साथ में !) का उपयोग करना शुरू करें, outभले ही आपका कोड बिना नल के हस्ताक्षर कर रहा हो,
  3. कोई वास्तविक प्रयोग करें Maybe<T>वापसी मान है, जो तब कभी नहीं है के रूप में प्रकार nullऔर लपेटता है कि boolऔर out Tमें HasValueऔर Valueगुण या कुछ इस तरह,
  4. एक टपल का उपयोग करें:
public static (bool success, T result) TryParse<T>(string s) => 


if (TryParse<MyClass>(someString) is (true, var result))
{
    // result is valid here, as success is true
}

मैं व्यक्तिगत रूप से उपयोग करने का पक्षधर हूं, Maybe<T>लेकिन यह एक डिकंस्ट्रक्ट का समर्थन करता है ताकि यह 4 के रूप में एक ट्यूपल के रूप में प्रतिमानित हो सके, ऊपर।


2
TryParse(someString) is {} myClass- यह सिंटैक्स कुछ उपयोग करने के लिए ले जाएगा, लेकिन मुझे यह विचार पसंद है।
सेबस्टियन रेडल

TryParse(someString) is var myClassमुझे आसान लगता है।
ओलिवियर जैकोट-डेसकोम्बर्स

2
@ OlivierJacot-Descombes यह आसान लग सकता है ... लेकिन यह काम नहीं करेगा। कार पैटर्न हमेशा मेल खाता है, इसलिए x is var yहमेशा सच होगा, चाहे xवह शून्य हो या न हो।
डेविड अरनो

15

यदि आप मेरी तरह थोड़ी देर से आ रहे हैं, तो यह पता चला है कि .NET टीम ने इसे अंतरिक्ष MaybeNullWhen(returnValue: true)में पैरामीटर विशेषताओं के एक समूह के माध्यम से संबोधित किया है, System.Diagnostics.CodeAnalysisजिसे आप ट्राइ पैटर्न के लिए उपयोग कर सकते हैं।

उदाहरण के लिए:

Dictionary.TryGetValue जैसा सामान्य कोड इससे कैसे निपटता है?

bool TryGetValue(TKey key, [MaybeNullWhen(returnValue: false)] out TValue value);

जिसका अर्थ है कि यदि आप चेक नहीं करते हैं तो आप चिल्लाते हैं true

// This is okay:
if(myDictionary.TryGetValue("cheese", out var result))
{
  var more = result * 42;
}

// But this is not:
_ = myDictionary.TryGetValue("cheese", out var result);
var more = result * 42;
// "CS8602: Dereference of a potentially null reference"

आगे की जानकारी:


3

मुझे नहीं लगता कि यहां कोई संघर्ष है।

आपकी आपत्ति

public static bool TryParse(string s, out MyClass? result);

है

क्योंकि मैं केवल शाखा में प्रवेश करता हूं जब ऑपरेशन सफल होता है, परिणाम उस शाखा में कभी भी अशक्त नहीं होना चाहिए।

हालाँकि, वास्तव में पुरानी शैली TryParse फ़ंक्शंस में आउट पैरामीटर को नल के असाइनमेंट को रोकने के लिए कुछ भी नहीं है।

जैसे।

MyJsonObject.TryParse("null", out obj) //sets obj to a null MyJsonObject and returns true

प्रोग्रामर को दी गई चेतावनी जब वे चेकिंग के बिना आउट पैरामीटर का उपयोग करते हैं तो सही है। आपको जाँच होनी चाहिए!

ऐसे मामलों का भार होने जा रहा है जहां आपको अशक्त प्रकारों को वापस करने के लिए मजबूर किया जाएगा जहां कोड की मुख्य शाखा एक गैर-अशक्त प्रकार लौटाती है। इनको स्पष्ट करने में आपकी सहायता करने के लिए चेतावनी केवल है। अर्थात।

MyClass? x = (new List<MyClass>()).FirstOrDefault(i=>i==1);

इसे कोड करने के लिए गैर-अशक्त तरीका एक अपवाद को फेंक देगा जहां एक अशक्त रहा होगा। चाहे आपका पार्सिंग, हो या फर्स्ट

MyClass x = (new List<MyClass>()).First(i=>i==1);

मुझे नहीं लगता कि FirstOrDefault, तुलना की जा सकती है क्योंकि इसकी वापसी मान के nullness है मुख्य संकेत। में TryParseतरीकों, अगर वापसी मान सच है अशक्त नहीं किया जा रहा बाहर पैरामीटर विधि अनुबंध का एक हिस्सा है।
सेबेस्टियन रेडल

अनुबंध का हिस्सा नहीं है। केवल एक ही चीज का आश्वासन दिया गया है कि कुछ बाहर पैरामीटर को सौंपा गया है
ईवान

यह वह व्यवहार है जिसकी मैं एक TryParseविधि से उम्मीद करता हूं । यदि IPAddress.TryParseकभी भी सही लौटा है, लेकिन इसके बाहर के पैरामीटर को गैर-शून्य नहीं सौंपा है, तो मैं इसे बग के रूप में रिपोर्ट करूंगा।
सेबेस्टियन रेडल

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

"आपकी उम्मीद समझ में आती है, लेकिन इसके संकलक द्वारा लागू नहीं किया गया है।" - मुझे पता है, लेकिन मेरा सवाल यह है कि मेरे पास जो अनुबंध है, उसे व्यक्त करने के लिए अपने कोड को सर्वोत्तम रूप से कैसे लिखें, न कि किसी अन्य कोड का अनुबंध क्या है।
सेबेस्टियन रेडल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.