इस (विस्तार आधारित पद्धति) का उपयोग कर के संभावित नुकसान आशुलिपि


15

सी # 6 अद्यतन

C#6 ?. is now a language feature में:

// C#1-5 
propertyValue1 = myObject != null ? myObject.StringProperty : null; 

// C#6 
propertyValue1 = myObject?.StringProperty; 

अभी भी नीचे दिए गए प्रश्न पुराने संस्करणों पर लागू होता है, लेकिन का उपयोग कर यदि एक नया आवेदन के विकास नया ?. ऑपरेटर बहुत बेहतर अभ्यास है।

मूल प्रश्न:

मैं नियमित रूप से संभवतः अशक्त वस्तुओं पर गुण का उपयोग करना चाहते हैं:

string propertyValue1 = null; 
if(myObject1 != null) 
    propertyValue1 = myObject1.StringProperty; 

int propertyValue2 = 0; 
if(myObject2 != null) 
    propertyValue2 = myObject2.IntProperty; 

और इसी तरह ...

मैं इतनी बार मैं एक है कि इस का उपयोग इसके लिए स्निपेट।

आप अगर एक इनलाइन साथ कुछ हद तक यह छोटा कर सकते हैं:

propertyValue1 = myObject != null ? myObject.StringProperty : null; 

हालांकि इस एक छोटे से भद्दा है, विशेष रूप से संपत्तियों की या यदि एक से अधिक स्तर बहुत स्थापना, अशक्त हो सकता है, उदाहरण के लिए, यदि:

int? i = SomeFunctionWhichMightReturnNull(); 
propertyValue2 = i ?? 0; 
:
propertyValue1 = myObject != null ? 
    (myObject.ObjectProp != null ? myObject.ObjectProp.StringProperty) : null : null; 

क्या मैं सच में चाहते ?? शैली वाक्य रचना, जो सीधे नल प्रकार के लिए महान काम करता है

तो मैं के साथ आया था निम्नलिखित:

public static TResult IfNotNull<T, TResult>(this T input, Func<T, TResult> action, TResult valueIfNull) 
    where T : class 
{ 
    if (input != null) return action(input); 
    else return valueIfNull; 
} 

//lets us have a null default if the type is nullable 
public static TResult IfNotNull<T, TResult>(this T input, Func<T, TResult> action) 
    where T : class 
    where TResult : class 
{ return input.IfNotNull(action, null); } 

यह इस वाक्य रचना की सुविधा देता है मुझे हमें:

propertyValue1 = myObject1.IfNotNull(x => x.StringProperty); 
propertyValue2 = myObject2.IfNotNull(x => x.IntProperty, 0); 

//or one with multiple levels 
propertyValue1 = myObject.IfNotNull( 
    o => o.ObjectProp.IfNotNull(p => p.StringProperty)); 

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

यह उदाहरण, बल्कि एक सरल एक है एक से थोड़ा अधिक जटिल एक से दो नल वस्तु गुण की तुलना की जाएगी:

if((obj1 == null && obj2 == null) || 
    (obj1 != null && obj2 != null && obj1.Property == obj2.Property)) 
    ... 

//becomes 
if(obj1.NullCompare(obj2, (x,y) => x.Property == y.Property) 
    ... 

क्या इस तरह से एक्सटेंशन के उपयोग का नुकसान कर रहे हैं? क्या अन्य कोडर भ्रमित होने की संभावना है? क्या यह सिर्फ एक्सटेंशन का दुरुपयोग है?


मुझे लगता है कि क्या मैं वास्तव में यहाँ चाहते हैं एक संकलक/भाषा विस्तार है:

propertyValue1 = myObject != null ? 
    (myObject.ObjectProp != null ? myObject.ObjectProp.StringProperty) : null 

//becomes 
propertyValue1 = myObject?ObjectProp?StringProperty; 

यह केवल मूल्य के लिए काम करेगा:

propertyValue1 = myObject != null ? myObject.StringProperty : null; 

//becomes 
propertyValue1 = myObject?StringProperty; 

इस परिसर में मामला काफी आसान होगा प्रकार, लेकिन आप शून्य समकक्षों को वापस कर सकते हैं:

int? propertyValue2 = myObject?ObjectProp?IntProperty; 

//or 

int propertyValue3 = myObject?ObjectProp?IntProperty ?? 0; 
16

हम स्वतंत्र रूप से के साथ आया था सटीक उसी एक्सटेंशन विधि का नाम और कार्यान्वयन: Null-propagating extension method। इसलिए हमें नहीं लगता कि यह भ्रमित है या विस्तार विधियों का दुरुपयोग है।

मैं के रूप में चेनिंग के साथ अपने "कई स्तरों" उदाहरण लिखते थे इस प्रकार है:

propertyValue1 = myObject.IfNotNull(o => o.ObjectProp).IfNotNull(p => p.StringProperty); 

एक now-closed bug on Microsoft Connect कि प्रस्तावित नहीं है "?।" एक नया सी # ऑपरेटर के रूप में जो इस शून्य प्रचार को निष्पादित करेगा। मैड्स टोरगर्सन (सी # भाषा टीम से) ने संक्षेप में समझाया कि वे इसे क्यों लागू नहीं करेंगे।

  0

हाँ के लिए दो तर्कों के साथ स्थिर विधियों को स्वीकार करने के लिए कोड को संशोधित करना होगा, हाँ, मैंने सीधे मैड से टेकएड पर पूछा - मूल रूप से यह सुविधा कटौती को याद रखती है, फिर भी वे इसे जोड़ सकते हैं एक भविष्य सी # संस्करण। 28 mar. 112011-03-28 13:20:02

  0

@ केथ अपडेट के लिए धन्यवाद! (यह सी # टीम को याद दिलाने के लिए चोट नहीं पहुंचा सकता है कि ग्राहकों को यह उपयोगी लगेगा।) 29 mar. 112011-03-29 02:33:02

  0

अब वे गंभीरता से इस पर विचार कर रहे हैं: http://blogs.msdn.com/b/jerrynixon/archive/2014/02/26/ अंत-सी-सी-हो रही-कभी-कभी-सुरक्षित-नेविगेशन-ऑपरेटर.aspx 28 feb. 142014-02-28 15:32:57

+1

"शून्य प्रचार" सुविधा की स्थिति "पूर्ण हो गई" है। Roslyn की [भाषा सुविधा कार्यान्वयन स्थिति] देखें (https://roslyn.codeplex.com/wikipage?title=Language%20Feature%20Status) 25 jul. 142014-07-25 17:01:25

  0

'? .' सी # 6 में कार्यान्वित किया गया है - मैंने इस सवाल को प्रतिबिंबित करने के लिए अद्यतन किया है । यहां दिए गए उत्तर अभी भी सी # 1-5 15 jun. 152015-06-15 06:41:02


0

व्यक्तिगत रूप से, अपने सभी स्पष्टीकरण के बाद भी मुझे याद नहीं कैसे हो इस काम करता है:

if(obj1.NullCompare(obj2, (x,y) => x.Property == y.Property) 

यह हो सकता है क्योंकि मैं कोई सी # अनुभव है; हालांकि, मैं आपके कोड में बाकी सब कुछ पढ़ और समझ सकता था। मैं कोड भाषा अज्ञेयवादी (esp। छोटी चीजों के लिए) रखना पसंद करता हूं ताकि कल, एक और डेवलपर मौजूदा भाषा के बारे में बहुत अधिक जानकारी के बिना इसे पूरी नई भाषा में बदल सकता है।


11

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


1

यह कोड को पढ़ने में थोड़ा आसान बनाता है, लेकिन वस्तु को विस्तारित करने की लागत पर। यह सब कुछ पर दिखाई देगा,

ध्यान दें कि आप वास्तव में कुछ भी नहीं बढ़ा रहे हैं (सैद्धांतिक रूप से छोड़कर)।

propertyValue2 = myObject2.IfNotNull(x => x.IntProperty, 0); 

आईएल कोड उत्पन्न होगा बिल्कुल के रूप में अगर यह लिखा गया था:

ExtentionClass::IfNotNull(myObject2, x => x.IntProperty, 0); 

कोई "भूमि के ऊपर" वस्तुओं को जोड़ा गया यह समर्थन करने के लिए।


5

कैसे पढ़ना और लिखना

if(myObject != null && myObject.ObjectProp != null) 
    propertyValue1 = myObject.ObjectProp.StringProperty; 

से जाफर हुसैन, एक श्रृंखला में अशक्त के लिए जाँच करने Runtime macros in C# 3 अभिव्यक्ति पेड़ का उपयोग करने का एक नमूना पोस्ट करने के लिए

propertyValue1 = myObject.IfNotNull(o => o.ObjectProp.IfNotNull(p => p.StringProperty)); 

आसान है।

हालांकि स्पष्ट रूप से प्रदर्शन प्रभाव पड़ता है। अब अगर हमारे पास संकलन समय पर ऐसा करने का कोई तरीका था।

  0

धन्यवाद, यह एक अच्छा लिंक है। 24 sep. 082008-09-24 08:02:29

+1

मैं आपकी पहली टिप्पणी से सहमत हूं। मुझे लगता है कि यह समस्या यह है कि यह कोड से तुरंत स्पष्ट नहीं है कि यह क्या कर रहा है। यह कम अव्यवस्थित है, लेकिन यह समझना आसान है कि आप जानते हैं कि यह क्या करता है_। 21 nov. 082008-11-21 10:34:51


5

मुझे बस इतना कहना है कि मुझे यह हैक पसंद है!

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

class Class1 
{ 
    public static readonly Class1 Empty = new Class1(); 
. 
. 
x = (obj1 ?? Class1.Empty).X; 
  0

स्वयं द्वारा विस्तार विधियां अधिक महंगी नहीं हैं, हालांकि, यह विशेष विस्तार विधि अधिक महंगी है, क्योंकि इसे लैम्ब्डा/अज्ञात विधि की आवश्यकता होती है। लैम्बडास पृष्ठभूमि में कक्षा आवंटन के लिए संकलित हो जाते हैं। इस प्रकार, यह अधिक महंगा है क्योंकि इसे आवंटन की आवश्यकता है। 29 jan. 092009-01-29 23:06:37

+1

@ जुडाह हिमांगो लैमदास केवल वर्ग आवंटन का पालन करते हैं यदि वे एक चर (एक बंद होने) को पकड़ते हैं। यदि वे नहीं करते हैं, तो वे एक कंपाइलर जेनरेटेड स्थिर विधि में बदल जाते हैं ... आप डॉटपीक (http://www.jetbrains.com/decompiler/) 24 feb. 142014-02-24 17:50:49


1

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


propertyValue1 = Util.IfNotNull(myObject1, x => x.StringProperty); 
propertyValue2 = Util.IfNotNull(myObject2, x => x.IntProperty, 0); 

"उपयोग"। grates, लेकिन आईएमओ कम वाक्य रचनात्मक बुराई है।

इसके अलावा, यदि आप इसे टीम के हिस्से के रूप में विकसित करते हैं, तो धीरे-धीरे पूछें कि दूसरों क्या सोचते हैं और करते हैं। अक्सर इस्तेमाल किए गए पैटर्न के लिए कोडबेस में संगति महत्वपूर्ण है।


1

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

string x = null; 
int len = x.IfNotNull(y => y.Length, 0); 

मैं यह सुनिश्चित करना चाहता हूं कि यह स्थैतिक विधि मूल्य प्रकारों पर काम करे जो शून्य हो सकती है, जैसे int?

संपादित करें: संकलक का कहना है कि इनमें से कोई भी मान्य हैं:

public void Test() 
    { 
     int? x = null; 
     int a = x.IfNotNull(z => z.Value + 1, 3); 
     int b = x.IfNotNull(z => z.Value + 1); 
    } 

उसके अलावा, इसके लिए जाओ।

  0

जैसे किसी संकलित डीएलएल को देखकर इसे सत्यापित कर सकते हैं, यही कारण है कि दो ओवरलोड - एक जिसे डिफ़ॉल्ट रूप से परिणाम प्रकार पर कोई बाधा नहीं होती है, और जिसे डिफ़ॉल्ट की आवश्यकता नहीं होती है लेकिन संदर्भ प्रकारों को परिणाम को बाधित करती है। 24 sep. 082008-09-24 08:00:20

  0

एक परीक्षण के लिए संपादित करें देखें। 24 sep. 082008-09-24 10:15:34

  0

ऐसा इसलिए है क्योंकि int? वास्तव में Nullable <int> के लिए संकलित है, जो वास्तव में एक संरचना है। यह केवल कंपाइलर जादू है जो आपको इसकी तुलना शून्य से करने देता है (यह सही तरीके से विफल रहता है जहां कक्षा: कक्षा बाधा)। मुझे Nullable <T> 26 sep. 082008-09-26 10:53:34

  0

पर एक और ओवरलोड विशिष्ट जोड़ने की आवश्यकता हो सकती है दूसरे विचारों पर - Nullable <int> में केवल दो गुण हैं: HasValue एक मान - दोनों में उपयोग किया जाता है ?? वाक्य - विन्यास। Int के लिए इसकी आवश्यकता नहीं होनी चाहिए? 06 oct. 082008-10-06 18:55:24


15

यहाँ एक और समाधान है, श्रृंखलित सदस्यों के लिए, विस्तार उपाय अपना सकते हैं:

public static U PropagateNulls<T,U> (this T obj 
            ,Expression<Func<T,U>> expr) 
{ if (obj==null) return default(U); 

    //uses a stack to reverse Member1(Member2(obj)) to obj.Member1.Member2 
    var members = new Stack<MemberInfo>(); 

    bool  searchingForMembers = true; 
    Expression currentExpression = expr.Body; 

    while (searchingForMembers) switch (currentExpression.NodeType) 
    { case ExpressionType.Parameter: searchingForMembers = false; break; 

      case ExpressionType.MemberAccess:  
      { var ma= (MemberExpression) currentExpression; 
      members.Push(ma.Member); 
      currentExpression = ma.Expression;   
      } break;  

      case ExpressionType.Call: 
      { var mc = (MethodCallExpression) currentExpression; 
      members.Push(mc.Method); 

      //only supports 1-arg static methods and 0-arg instance methods 
      if ( (mc.Method.IsStatic && mc.Arguments.Count == 1) 
       || (mc.Arguments.Count == 0)) 
      { currentExpression = mc.Method.IsStatic ? mc.Arguments[0] 
                : mc.Object; 
       break; 
      } 

      throw new NotSupportedException(mc.Method+" is not supported"); 
     } 

     default: throw new NotSupportedException 
         (currentExpression.GetType()+" not supported"); 
    } 

    object currValue = obj; 
    while(members.Count > 0) 
    { var m = members.Pop(); 

     switch(m.MemberType) 
     { case MemberTypes.Field: 
      currValue = ((FieldInfo) m).GetValue(currValue); 
      break; 

     case MemberTypes.Method: 
      var method = (MethodBase) m; 
      currValue = method.IsStatic 
           ? method.Invoke(null,new[]{currValue}) 
           : method.Invoke(currValue,null); 
      break; 

     case MemberTypes.Property: 
      var method = ((PropertyInfo) m).GetGetMethod(true); 
       currValue = method.Invoke(currValue,null); 
      break; 

     }  

     if (currValue==null) return default(U); 
    } 

    return (U) currValue;  
} 

तो फिर तुम यह कर सकते हैं जहां किसी भी अशक्त, या कोई भी हो सकता है:

foo.PropagateNulls(x => x.ExtensionMethod().Property.Field.Method()); 
+1

बहुत अंतर्दृष्टि। धन्यवाद! 18 dec. 082008-12-18 00:58:20

  0

मुझे इस विचार से प्यार है! हालांकि, यह (साथ ही साथ अन्य सभी समाधान) LINQ कथन के भीतर काम नहीं कर रहा है। उदाहरण के लिए जब कोई कर रहा है। जैसे किसी नए अनाम प्रकार में चयन करें। चयन करें (एस => नया {MyNewProperty = s.PropogateNulls (p => p.Thing)})। यह काम नहीं करता है। अभी भी पुराने नल की जांच करनी है। 22 mar. 162016-03-22 23:24:17

  0

आपको 'तर्कसंगत। चयन (src, lambda) ' 23 mar. 162016-03-23 05:31:48


0

यहाँ, myObject.NullSafe (x => x.SomeProperty.NullSafe (x => x.SomeMethod)) का उपयोग कर एक और उपाय है नहीं सटीक प्रश्न का उत्तर पूछा http://www.epitka.blogspot.com/

  0

पर लागू होते हैं, लेकिन यह वही काम करने के लिए बहुत अधिक कोड है। इसके अलावा आपकी हो सकता है कि वर्ग ढांचे के नालीबल <T> के समान ही है और इनवॉक का उपयोग करके आप एक अनावश्यक प्रदर्शन हिट जोड़ते हैं। फिर भी - एक ही समस्या पर वैकल्पिक लेना अच्छा लगता है। 28 may. 092009-05-28 22:18:56


1

में बताया गया है, लेकिन वहाँ शून्य-सशर्त ऑपरेटरin C# 6.0 है। मैं बहस कर सकते हैं के बाद से सी # 6.0 :)

तो अपने अभिव्यक्ति सरल है ओपी में विकल्प का उपयोग करने के लिए एक गरीब विकल्प हो जाएगा,

string propertyValue = myObject?.StringProperty; 

मामले myObject में अशक्त यह शून्य देता है। मामले में संपत्ति एक मान प्रकार आप बराबर नल प्रकार, जैसे उपयोग करने के लिए है,

int? propertyValue = myObject?.IntProperty; 

या अन्यथा आप अशक्त के मामले में एक डिफ़ॉल्ट मान देने के लिए अशक्त कोलेसिंग ऑपरेटर के साथ सम्मिलित कर सकते हैं।उदाहरण के लिए,

int propertyValue = myObject?.IntProperty ?? 0; 

?. केवल वाक्य रचना उपलब्ध नहीं है। अनुक्रमित गुणों के लिए आप ?[..] का उपयोग कर सकते हैं। उदाहरण के लिए, ?. ऑपरेटर की

string propertyValue = myObject?[index]; //returns null in case myObject is null 

एक आश्चर्य की बात है कि यह व्यवहार होशियारी बाद .Member कॉल बायपास कर सकते हैं अगर वस्तु अशक्त होने वाला है। ऐसा ही एक उदाहरण कड़ी में दी गई है:

var result = value?.Substring(0, Math.Min(value.Length, length)).PadRight(length); 

इस मामले result में अशक्त है अगर value रिक्त है और value.Length अभिव्यक्ति NullReferenceException में परिणाम नहीं होगा।