2009-05-12 9 views
13

मैं गहरी लम्दा अभिव्यक्ति में नल की जांच कैसे कर सकता हूं?गहरी लैम्ब्डा अभिव्यक्ति में नल के लिए कैसे जांचें?

कहते हैं, उदाहरण के लिए मैं एक वर्ग संरचना है कि नेस्ट किया गया था कई परतों गहरी है, और मुझे निम्नलिखित लैम्ब्डा निष्पादित करने के लिए चाहता था:

x => x.Two.Three.Four.Foo 

मैं यह शून्य वापस जाने के लिए अगर दो, तीन, या चार अशक्त थे चाहते हैं , System.ullReferenceException फेंकने के बजाए।

public class Tests 
{ 
    // This test will succeed 
    [Fact] 
    public void ReturnsValueWhenClass2NotNull() 
    { 
     var one = new One(); 
     one.Two = new Two(); 
     one.Two.Three = new Three(); 
     one.Two.Three.Four = new Four(); 
     one.Two.Three.Four.Foo = "blah"; 

     var result = GetValue(one, x => x.Two.Three.Four.Foo); 

     Assert.Equal("blah", result); 
    } 

    // This test will fail 
    [Fact] 
    public void ReturnsNullWhenClass2IsNull() 
    { 
     var one = new One(); 

     var result = GetValue(one, x => x.Two.Three.Four.Foo); 

     Assert.Equal(null, result); 
    } 

    private TResult GetValue<TModel, TResult>(TModel model, Expression<Func<TModel, TResult>> expression) 
    { 
     var func = expression.Compile(); 
     var value = func(model); 
     return value; 
    } 

    public class One 
    { 
     public Two Two { get; set; } 
    } 

    public class Two 
    { 
     public Three Three { get; set; } 
    } 

    public class Three 
    { 
     public Four Four { get; set; } 
    } 

    public class Four 
    { 
     public string Foo { get; set; } 
     public string Bar { get; set; } 
    } 
} 

अद्यतन:

एक समाधान इस तरह NullReferenceException को पकड़ने के लिए होगा:

private TResult GetValue<TModel, TResult>(TModel model, Expression<Func<TModel, TResult>> expression) 
    { 
     TResult value; 
     try 
     { 
      var func = expression.Compile(); 
      value = func(model); 
     } 
     catch (NullReferenceException) 
     { 
      value = default(TResult); 
     } 
     return value; 
    } 

लेकिन मैं एक अपवाद है कि नहीं है, मेरे मन में पकड़ने की कीमत उठाना नफरत है, असाधारण। मुझे उम्मीद है कि यह मेरे डोमेन में अक्सर मामला होगा।

अद्यतन 2:

एक अन्य समाधान इस तरह संपत्ति ही टिककर खेल को संशोधित होगा:

public class One 
    { 
     private Two two; 
     public Two Two 
     { 
      get 
      { 
       return two ?? new Two(); 
      } 
      set 
      { 
       two = value; 
      } 
     } 
    } 

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

public static class Get { 
    public static T IfNotNull<T, U>(this U item, Func<U, T> lambda) where U: class { 
     if (item == null) { 
      return default(T); 
     } 
     return lambda(item); 
    } 
} 

var one = new One(); 
string fooIfNotNull = one.IfNotNull(x => x.Two).IfNotNull(x => x.Three).IfNotNull(x => x.Four).IfNotNull(x => x.Foo); 

उत्तर

12

आप इसे संक्षिप्त तरीके से नहीं कर सकते हैं। आप लैम्ब्डा कई पंक्तियों बनाने सकते हैं, या नेस्टेड त्रिगुट ऑपरेटर का उपयोग:

var result = GetValue(one, x => x.Two == null ? null : 
           x.Two.Three == null ? null : 
           x.Two.Three.Four == null ? null : 
           x.Two.Three.Four.Foo; 

बदसूरत, मुझे पता है।

+2

यह बहुत अधिक संक्षिप्त है। Maybe (x => x.Two.Three.Four.Foo); http://maybe.codeplex.com/ – Maslow

17

आप एक सामान्य सहायक विस्तार विधि, की तरह कुछ के साथ ऐसा कर सकता है। कक्षा वन, दो, तीन और चार में एक कन्स्ट्रक्टर जोड़ें। कन्स्ट्रक्टर में आपकी गुणों को आरंभ करें ताकि वे शून्य न हों।

+0

उस स्थिति में, मैं किसी भी प्रकार के फू या बार के लिए डिफ़ॉल्ट मान वापस करना चाहता हूं। जो मैं वास्तव में टालना चाहता हूं वह अपवाद है अगर अभिव्यक्ति के पेड़ में कुछ और शून्य हो गया था। – JohnRudolfLewis

+0

मैंने अपना जवाब संपादित किया और एक कोड नमूना जोड़ा, जो ठीक से संकलित करता है और चाल करना चाहिए। – Lucero

+0

इतना आसान है? वह सुरुचिपूर्ण? +1 और प्रतिबिंब के कारण कोई रनटाइम प्रदर्शन हिट नहीं हुआ। क्या किसी ने गेबे के समाधान या 'सामान्य' दृष्टिकोण के खिलाफ इसे बेंचमार्क किया था? –

0

हमेशा उन्हें उपयोग करने से पहले अपने गुण प्रारंभ:

+0

मैं आमतौर पर करता हूं, लेकिन इस डोमेन में, गुण कभी-कभी शून्य पर सेट हो सकते हैं। यह वैध व्यवहार है। – JohnRudolfLewis

0

आप की तरह कुछ पढ़ने के लिए अपने ही टिककर खेल को संशोधित कर सकता है: सी # में

private Two _two; 
public Two Two 
{ 
    get 
    { 
     if (null == _two) 
     return new Two(); 
     else 
     return _two; 
     } 
} 
+0

क्लाइंट कोड में कुछ पंक्तियों को सहेजने के लिए कार्यान्वयन को संशोधित करना सभी प्रकार के अलार्म को आग लगाना चाहिए। –

+1

मैं इस बात से असहमत हूं कि यह एक मुद्दा है: मैं इस रक्षात्मक कोडिंग को कॉल करूंगा।उपर्युक्त कोड यह सुनिश्चित करता है कि किसी संपत्ति का मूल्य उस ज्ञान/वस्तु के किसी उपभोक्ता के साथ उस ज्ञान को साझा किए बिना कभी भी शून्य न हो। –

+1

यदि मैं दो बार कॉल करता रहता हूं तो _two शून्य है, मैं * नए * उदाहरण प्राप्त कर रहा हूं ... ew – Lucas

2

मैं कुशल नहीं कर रहा हूँ, लेकिन शायद वहाँ माणिक से "andand" पैटर्न लागू करने के लिए कोई रास्ता है कि प्रदूषण फैलाने के बिना वास्तव में इस समस्या का हल है कार्यान्वयन।

अवधारणा को हास्केल में शायद मोनाड के रूप में भी जाना जाता है।

this लेख का शीर्षक आशाजनक प्रतीत होता है।

+1

दिलचस्प, यह लेख लगभग समाधान के समान है जो मैं इसके बारे में सोचकर आया था, मेरी पोस्ट देखें ... – Lucero

+0

वाह, पूरी तरह से इसे याद किया, मुझे लगता है कि मैं "शायद" –

+0

http: // हो सकता है। codeplex.com/ यह कर सकते हैं। – Maslow

0

मुझे कभी-कभी सहकारी ऑपरेटर उपयोगी होता है। यह केवल तभी मदद करता है जब आप उस वस्तु के डिफ़ॉल्ट/शून्य समकक्ष संस्करण को छोड़ सकते हैं जिसमें आप ड्रॉप कर सकते हैं।

उदाहरण के लिए, कभी-कभी जब मैं खुले XML को क्रैक कर रहा हूं ...

IEnumeratable<XElement> sample; 
sample.Where(S => (S.Attribute["name"] ?? new XAttribute("name","")).Value.StartsWith("Hello"))... 

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

7

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

+0

यह बहुत अच्छा होगा! डेल्फी प्रिज्म अपने ":" ऑपरेटर के साथ समान है: http://prismwiki.codegear.com/en/Colon_Operator –

+0

मैं दूसरा हूं। इससे बहुत सारे कोड बहुत साफ हो जाएंगे! – mdonatas

+0

यह सुविधा अब सी # 6 में है! –

4

अब आप कोडप्लेक्स पर Maybe प्रोजेक्ट का उपयोग कर सकते हैं।

सिंटेक्स है:

string result = One.Maybe(o => o.Two.Three.Four.Foo); 

string cityName = Employee.Maybe(e => e.Person.Address.CityName); 
+0

अभिव्यक्ति वृक्ष प्रभाव प्रदर्शन का उपयोग करता है? –

+3

प्रदर्शन विशेषताओं में कुछ अलग बदलाव कर रहा है? हाँ। क्या यह वास्तव में पर्याप्त है कि आप या उपयोगकर्ता नोटिस करेंगे? मैं आपका उपयोग नहीं जानता, प्रोफाइल करें। – Maslow

+0

क्या आपके पास अपने (या किसी और के) उपयोग का कोई प्रदर्शन आंकड़ा है? –

0

मैं एक समारोह है कि बयान वर्गों है कि मैं एक XSD कि 4 और 5 स्तर नीचे हैं से बदल के लिए .IFNotNull विधि के लिए nulls से बचने के लिए यदि का एक बहुत इस्तेमाल किया बदल दिया।

ProdYear.PRIOR_CUMULATIVE_CARBON_DIOXIDE_VALUE = year.IfNotNull(x => x.PRIOR_CUMULATIVE).IfNotNull(y => y.CARBON_DIOXIDE).IfNotNull(z => z.VALUE).ToDouble(); 
ProdYear.PRIOR_CUMULATIVE_CARBON_DIOXIDE_UOM = year.IfNotNull(x => x.PRIOR_CUMULATIVE).IfNotNull(y => y.CARBON_DIOXIDE).IfNotNull(z => z.UOM); 

यहाँ इस बारे में कुछ दिलचस्प आँकड़े हैं::

1) इस नई विधि है कि यदि साथ भिन्नता को चलाने के लिए ले लिया 3.7409 गुना अधिक

यहाँ परिवर्तित कोड की कुछ लाइनें हैं बयान।
2) मैंने 157 से 59 तक अपनी फ़ंक्शन लाइन गिनती घटा दी।
3) 0EDevExpress से "रखरखाव जटिलता" स्कोर है। जब मैं लैम्ब्डा कथन में परिवर्तित हुआ, तो यह 984 से 2076 तक बढ़ गया, जो सैद्धांतिक रूप से बनाए रखने के लिए बहुत कठिन है।

3

मैं एक विस्तार विधि है जो आप ऐसा करने में सक्षम बनाता है लिखा है:

blah.GetValueOrDefault(x => x.Two.Three.Four.Foo); 

यह अभिव्यक्ति पेड़ का उपयोग करता है अभिव्यक्ति मूल्य लौटने से पहले प्रत्येक नोड पर nulls के लिए एक नेस्टेड सशर्त जाँच निर्माण करने के लिए; बनाया गया अभिव्यक्ति पेड़ Func और कैश किए गए संकलित किया गया है, इसलिए उसी कॉल के बाद के उपयोग लगभग मूल गति से चलने चाहिए।

आपको एक डिफ़ॉल्ट मूल्य प्रेषित यदि आप चाहें तो वापस जाने के लिए कर सकते हैं:

blah.GetValueOrDefault(x => x.Two.Three.Four.Foo, Foo.Empty); 

मैं इसे here बारे में एक ब्लॉग लिखा है।

संबंधित मुद्दे