सी # सुरुचिपूर्ण तरीके से यह जांचने के लिए कि किसी संपत्ति की संपत्ति शून्य है या नहीं


98

C # में, कहें कि आप इस उदाहरण में PropertyC का मान खींचना चाहते हैं और ObjectA, PropertyA और PropertyB सभी शून्य हो सकते हैं।

ObjectA.PropertyA.PropertyB.PropertyC

मैं कम से कम कोड वाली प्रॉपर्टी को सुरक्षित रूप से कैसे प्राप्त कर सकता हूं?

अभी मैं जाँच करूँगा:

if(ObjectA != null && ObjectA.PropertyA !=null && ObjectA.PropertyA.PropertyB != null)
{
    // safely pull off the value
    int value = objectA.PropertyA.PropertyB.PropertyC;
}

इस तरह कुछ और करना अच्छा होगा (छद्म कोड)।

int value = ObjectA.PropertyA.PropertyB ? ObjectA.PropertyA.PropertyB : defaultVal;

संभवतः एक अशक्त-सह-संचालक के साथ और भी अधिक ढह गई।

EDIT मूल रूप से मैंने कहा कि मेरा दूसरा उदाहरण js जैसा था, लेकिन मैंने इसे psuedo-code में बदल दिया क्योंकि यह सही ढंग से बताया गया था कि यह js में काम नहीं करेगा।


मैं यह नहीं देखता कि आपका js उदाहरण कैसे काम करता है। जब भी ObjectAया PropertyAअशक्त हों, आपको "ऑब्जेक्ट अपेक्षित" त्रुटि मिलनी चाहिए ।
lincolnk

जवाबों:


120

C # 6 में आप Null Conditional Operator का उपयोग कर सकते हैं । तो मूल परीक्षा होगी:

int? value = objectA?.PropertyA?.PropertyB?.PropertyC;

2
क्या आप बता सकते हैं कि यह क्या करता है? शून्य valueहोने पर क्या समान है PropertyC? या यदि PropertyBअशक्त है? क्या हुआ अगर Object Aअशक्त है?
कोलोब कैनियन

1
यदि इनमें से कोई भी गुण शून्य है, तो संपूर्ण विवरण के रूप में वापस आ जाता है null। यह बाएं से दाएं शुरू होता है। सिंटैक्टिकल शुगर के बिना अगर यह बयानों की एक श्रृंखला के बराबर है, जहां if(propertyX == null) {value = null} else if (propertyY == null){ value = null} else if......अंतिम अंतिम अभिव्यक्ति के साथif(propertyZ != null) { value = propertyZ }
डिटेक्टिवपिकाचू

27

लघु विस्तार विधि:

public static TResult IfNotNull<TInput, TResult>(this TInput o, Func<TInput, TResult> evaluator)
  where TResult : class where TInput : class
{
  if (o == null) return null;
  return evaluator(o);
}

का उपयोग करते हुए

PropertyC value = ObjectA.IfNotNull(x => x.PropertyA).IfNotNull(x => x.PropertyB).IfNotNull(x => x.PropertyC);

यह सरल विस्तार विधि और बहुत कुछ आप पर पा सकते हैं http://devtalk.net/csharp/chained-null-checks-and-the-maybe-monad/

संपादित करें:

क्षण के लिए इसका उपयोग करने के बाद मुझे लगता है कि इस विधि के लिए उचित नाम मूल With () के बजाय IfNotNull () होना चाहिए ।


15

क्या आप अपनी कक्षा में एक विधि जोड़ सकते हैं? यदि नहीं, तो क्या आपने विस्तार विधियों का उपयोग करने के बारे में सोचा है? आप अपने ऑब्जेक्ट प्रकार के लिए एक एक्सटेंशन विधि बना सकते हैं जिसे कहा जाता हैGetPropC()

उदाहरण:

public static class MyExtensions
{
    public static int GetPropC(this MyObjectType obj, int defaltValue)
    {
        if (obj != null && obj.PropertyA != null & obj.PropertyA.PropertyB != null)
            return obj.PropertyA.PropertyB.PropertyC;
        return defaltValue;
    }
}

उपयोग:

int val = ObjectA.GetPropC(0); // will return PropC value, or 0 (defaltValue)

वैसे, यह मानता है कि आप .NET 3 या उच्चतर का उपयोग कर रहे हैं।


11

जिस तरह से आप कर रहे हैं वह सही है।

आप यहाँ बताए गए ट्रिक का उपयोग कर सकते हैं , जो Linq व्यंजक का उपयोग कर रहा है:

int value = ObjectA.NullSafeEval(x => x.PropertyA.PropertyB.PropertyC, 0);

लेकिन यह बहुत धीमी है कि मैन्युअल रूप से प्रत्येक संपत्ति की जाँच कर रहा है ...


10

कानून के कानून का पालन ​​करने के लिए रिफ्लेक्टर


जब आप केवल प्रॉपर्टी पढ़ रहे हों तो रिफैक्टरिंग की आवश्यकता के लिए मैं केवल तीन स्तरों पर एक वस्तु ग्राफ पर विचार नहीं करता। यदि ओपी प्रॉपर्टी के माध्यम से संदर्भित ऑब्जेक्ट पर एक विधि को कॉल करना चाहता था, तो मैं सहमत हूं लेकिन जब यह एक संपत्ति है जिसे केवल पढ़ने के लिए नल की जांच की आवश्यकता है। इस उदाहरण में यह Customer.Address.Country के समान सरल हो सकता है जहाँ देश एक संदर्भ प्रकार जैसे KeyValuePair हो सकता है। आप नल रेफरी चेक की आवश्यकता को रोकने के लिए इसे कैसे रिफ्लेक्टर करेंगे?
डैरेन लुईस

ओपी उदाहरण वास्तव में 4 गहरा है। मेरा सुझाव अशक्त रेफरी चेकों को हटाने के लिए नहीं है, लेकिन वस्तुओं में उन्हें खोजने के लिए सबसे अधिक संभावना है कि उन्हें ठीक से संभालने में सक्षम हो। अधिकांश "अंगूठे के नियम" की तरह अपवाद हैं लेकिन मुझे यकीन नहीं है कि यह एक है। क्या हम असहमत होने के लिए सहमत हो सकते हैं?
रैटलबोट

4
मैं @rtalbot के साथ सहमत हूँ (हालाँकि, निष्पक्षता में @Daz लुईस एक 4-गहन उदाहरण पेश कर रहा है, क्योंकि अंतिम आइटम एक KeyValuePair है)। यदि कोई ग्राहक वस्तु के साथ कुछ गड़बड़ कर रहा है, तो मुझे पता नहीं है कि पता वस्तु-उत्तराधिकार के माध्यम से उसका क्या व्यवसाय है। मान लीजिए कि आप बाद में निर्णय लेते हैं कि KeyValuePair देश की संपत्ति के लिए इतना बड़ा विचार नहीं था। उस स्थिति में, सभी का कोड बदलना होगा। यह एक अच्छा डिज़ाइन नहीं है।
जेफरी एल व्हाइटलेज

10

अद्यतन 2014: C # 6 में एक नया ऑपरेटर है ?.जिसे 'सुरक्षित नेविगेशन' या 'अशक्त प्रचार' कहा जाता है।

parent?.child

Http://blogs.msdn.com/b/jerrynixon/archive/2014/02/26/at-last-c-is-getting-sometimes-called-the-safe-navigation-operator.aspx पढ़ें विवरण के लिए

यह लंबे समय से एक बेहद लोकप्रिय अनुरोध किया गया है https://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/3990187-add-operator-to-c-?tracking_code=594c10a522f8e9bc987ee4a5e2c0b38d


8

आप स्पष्ट रूप से अशक्त मोनाड की तलाश कर रहे हैं :

string result = new A().PropertyB.PropertyC.Value;

हो जाता है

string result = from a in new A()
                from b in a.PropertyB
                from c in b.PropertyC
                select c.Value;

यह रिटर्न null, यदि कोई भी अशक्त गुण शून्य है; अन्यथा, का मूल्य Value

class A { public B PropertyB { get; set; } }
class B { public C PropertyC { get; set; } }
class C { public string Value { get; set; } }

LINQ विस्तार विधियाँ:

public static class NullableExtensions
{
    public static TResult SelectMany<TOuter, TInner, TResult>(
        this TOuter source,
        Func<TOuter, TInner> innerSelector,
        Func<TOuter, TInner, TResult> resultSelector)
        where TOuter : class
        where TInner : class
        where TResult : class
    {
        if (source == null) return null;
        TInner inner = innerSelector(source);
        if (inner == null) return null;
        return resultSelector(source, inner);
    }
}

विस्तार विधि यहाँ क्यों है? इसका उपयोग नहीं हो रहा है।
म्लादेन मिहाजलोविक

1
@MladenMihajlovic: SelectManyविस्तार पद्धति का उपयोग from ... in ... from ... in ...वाक्य रचना द्वारा किया जाता है ।
dtb

5

मान लें कि आपके पास एक प्रकार के रिक्त मान हैं, यह होगा:

var x = (((objectA ?? A.Empty).PropertyOfB ?? B.Empty).PropertyOfC ?? C.Empty).PropertyOfString;

मैं C # का बहुत बड़ा प्रशंसक हूं, लेकिन नए जावा (1.7?) में बहुत अच्छी बात है? ऑपरेटर:

 var x = objectA.?PropertyOfB.?PropertyOfC.?PropertyOfString;

1
यह वास्तव में जावा 1.7 में होने जा रहा है? यह लंबे समय से C # में अनुरोध किया गया है, लेकिन मुझे संदेह है कि यह कभी भी होगा ...
थॉमस लेवेस्क

दुर्भाग्य से मेरे पास खाली मूल्य नहीं हैं। कि जावा वाक्यविन्यास हालांकि मीठा लगता है! मैं इसे अप-वोट करने जा रहा हूं, सिर्फ इसलिए कि मुझे वह सिंटैक्स चाहिए!
जॉन क्रैग

3
थॉमस: पिछली बार जब मैंने Tech.puredanger.com/java7 को चेक किया था तो लगा था कि यह जावा को मिलेगा। लेकिन अब जब मैं इसकी पुनरावृत्ति करता हूं तो कहता है: अशक्त सुरक्षित संचालन: सं। इसलिए मैंने अपने बयान को रद्द कर दिया और इसे एक नए के साथ बदल दिया: यह जावा 1.7 के लिए प्रस्तावित किया गया था, लेकिन इसे नहीं बनाया।
बस एक और मेटाप्रोग्रामर

एक अतिरिक्त दृष्टिकोण monad.net द्वारा उपयोग किया जाने वाला एक है
एक और

1
लगता है? ऑपरेटर विजुअल स्टूडियो 2015 https://msdn.microsoft.com/en-us/library/dn986595.aspx में है
एडवर्ड

4

यह कोड "कम से कम राशि का कोड" है, लेकिन सबसे अच्छा अभ्यास नहीं:

try
{
    return ObjectA.PropertyA.PropertyB.PropertyC;
}
catch(NullReferenceException)
{
     return null;
}

1
मैंने इस अलॉट की तरह कोड देखा है और प्रदर्शन के नुकसान को नापसंद करते हुए सबसे बड़ा मुद्दा यह है कि यह डिबगिंग को जटिल बनाता है क्योंकि असली अपवाद लाखों बेकार नल रेफरी अपवादों में डूब जाता है।
सिर्फ एक अन्य मेटाप्रोग्रामर

कभी-कभी यह 3 साल बाद मेरे खुद के जवाब को पढ़ने के लिए मनोरंजक है मुझे लगता है, मैं आज अलग तरीके से जवाब दूंगा। मैं कहूंगा कि कोड Demeter कानून का उल्लंघन करता है और मैं इसे फिर से शुरू करने की सिफारिश करूंगा ताकि यह न हो।
बोरिस मोदिलेव्स्की

1
आज के मूल उत्तर के 7 साल बाद, मैं @Phillip Ngan में शामिल होऊंगा और C # 6 का उपयोग निम्नलिखित सिंटैक्स के साथ करूँगा? मान = objectA .PropertyA? .PropertyB? .PropertyC;
बोरिस मोदिलेव्स्की

4

जब मुझे उस तरह से कॉल करने की आवश्यकता होती है, तो मैं एक सहायक विधि पर भरोसा करता हूं जिसे मैंने बनाया, TryGet ():

    public static U TryGet<T, U>(this T obj, Func<T, U> func)
    {
        return obj.TryGet(func, default(U));
    }

    public static U TryGet<T, U>(this T obj, Func<T, U> func, U whenNull)
    {
        return obj == null ? whenNull : func(obj);
    }

आपके मामले में, आप इसका इस तरह उपयोग करेंगे:

    int value = ObjectA
        .TryGet(p => p.PropertyA)
        .TryGet(p => p.PropertyB)
        .TryGet(p => p.PropertyC, defaultVal);

मुझे नहीं लगता कि यह कोड काम करता है। DefaultVal का प्रकार क्या है? var p = नया व्यक्ति (); Assert.AreEqual (p। TryGet (x => x.FirstName) .TryGet (x => x.LastName)
कीथ

मैंने जो उदाहरण लिखा है, उसे इस तरह से पढ़ा जाना चाहिए: ObjectA.PropertyA.PropertyB.PropertyC। आपका कोड "FirstName" से "LastName" नामक एक संपत्ति को लोड करने की कोशिश कर रहा है, जो कि इच्छित उपयोग नहीं है। एक और सही उदाहरण कुछ इस तरह होगा: var postcode = person.TryGet (p => p.Address) .TryGet (p => p.Postcode); वैसे, मेरा TryGet () सहायक विधि C # 6.0 में एक नई सुविधा के समान है - अशक्त-संचालक। इसका उपयोग इस तरह होगा: var पोस्टकोड = व्यक्ति?; .ड्रेस? .Postcode; msdn.microsoft.com/en-us/magazine/dn802602.aspx
Emanuel

3

मैंने नए C # 6.0 में कुछ देखा, यह 'का उपयोग करके है?' बजाय जाँच के

उदाहरण के लिए उपयोग करने के बजाय

if (Person != null && Person.Contact!=null && Person.Contact.Address!= null && Person.Contact.Address.City != null)
{ 
  var city = person.contact.address.city;
}

आप बस उपयोग करें

var city = person?.contact?.address?.city;

मुझे उम्मीद है कि इसने किसी की मदद की।


अपडेट करें:

अब आप ऐसा कर सकते हैं

 var city = (Person != null)? 
           ((Person.Contact!=null)? 
              ((Person.Contact.Address!= null)?
                      ((Person.Contact.Address.City!=null)? 
                                 Person.Contact.Address.City : null )
                       :null)
               :null)
            : null;

1

आप ऐसा कर सकते हैं:

class ObjectAType
{
    public int PropertyC
    {
        get
        {
            if (PropertyA == null)
                return 0;
            if (PropertyA.PropertyB == null)
                return 0;
            return PropertyA.PropertyB.PropertyC;
        }
    }
}



if (ObjectA != null)
{
    int value = ObjectA.PropertyC;
    ...
}

या इससे भी बेहतर हो सकता है:

private static int GetPropertyC(ObjectAType objectA)
{
    if (objectA == null)
        return 0;
    if (objectA.PropertyA == null)
        return 0;
    if (objectA.PropertyA.PropertyB == null)
        return 0;
    return objectA.PropertyA.PropertyB.PropertyC;
}


int value = GetPropertyC(ObjectA);

1

आप निम्नलिखित एक्सटेंशन का उपयोग कर सकते हैं और मुझे लगता है कि यह वास्तव में अच्छा है:

/// <summary>
/// Simplifies null checking
/// </summary>
public static TR Get<TF, TR>(TF t, Func<TF, TR> f)
    where TF : class
{
    return t != null ? f(t) : default(TR);
}

/// <summary>
/// Simplifies null checking
/// </summary>
public static TR Get<T1, T2, TR>(T1 p1, Func<T1, T2> p2, Func<T2, TR> p3)
    where T1 : class
    where T2 : class
{
    return Get(Get(p1, p2), p3);
}

/// <summary>
/// Simplifies null checking
/// </summary>
public static TR Get<T1, T2, T3, TR>(T1 p1, Func<T1, T2> p2, Func<T2, T3> p3, Func<T3, TR> p4)
    where T1 : class
    where T2 : class
    where T3 : class
{
    return Get(Get(Get(p1, p2), p3), p4);
}

और इसका उपयोग इस तरह किया जाता है:

int value = Nulify.Get(objectA, x=>x.PropertyA, x=>x.PropertyB, x=>x.PropertyC);

0

मैं Nulla प्रकार के समान पैटर्न का उपयोग करके संपत्ति के प्रकार (या यदि आपके प्रकार नहीं है तो एक विस्तार विधि) में अपनी खुद की विधि लिखूंगा।

class PropertyAType
{
   public PropertyBType PropertyB {get; set; }

   public PropertyBType GetPropertyBOrDefault()
   {
       return PropertyB != null ? PropertyB : defaultValue;
   }
}

ठीक है, उस स्थिति में, स्पष्ट रूप से प्रॉपर्टीबी कभी भी अशक्त नहीं हो सकती है।
पुनरावर्ती

0

बस इस पद के लिए ठोकर खाई।

कुछ समय पहले मैंने एक नए ???ऑपरेटर को जोड़ने के बारे में विज़ुअल स्टूडियो कनेक्ट पर एक सुझाव दिया था ।

http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/4104392-add-as-an-recursive-null-reference-check-opera

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

string product_name = Order.OrderDetails[0].Product.Name ??? "no product defined";

इस कोड में

Func<string> _get_default = () => "no product defined"; 
string product_name = Order == null 
    ? _get_default.Invoke() 
    : Order.OrderDetails[0] == null 
        ? _get_default.Invoke() 
        : Order.OrderDetails[0].Product == null 
            ? _get_default.Invoke() 
            : Order.OrderDetails[0].Product.Name ?? _get_default.Invoke()

अशक्त जाँच के लिए ऐसा लग सकता है

bool isNull = (Order.OrderDetails[0].Product ??? null) == null;

0

मैंने एक विधि लिखी है जो एक डिफ़ॉल्ट मान को स्वीकार करता है, यहाँ इसका उपयोग कैसे किया जाए:

var teacher = new Teacher();
return teacher.GetProperty(t => t.Name);
return teacher.GetProperty(t => t.Name, "Default name");

यहाँ कोड है:

public static class Helper
{
    /// <summary>
    /// Gets a property if the object is not null.
    /// var teacher = new Teacher();
    /// return teacher.GetProperty(t => t.Name);
    /// return teacher.GetProperty(t => t.Name, "Default name");
    /// </summary>
    public static TSecond GetProperty<TFirst, TSecond>(this TFirst item1,
        Func<TFirst, TSecond> getItem2, TSecond defaultValue = default(TSecond))
    {
        if (item1 == null)
        {
            return defaultValue;
        }

        return getItem2(item1);
    }
}

1
यह समाधान पहले से ही अन्य उत्तरों (बार-बार) में प्रदान किया गया है। इसे फिर से पोस्ट करने का कोई कारण नहीं है ।
सर्विक्स

मुझे कोई भी ऐसा डिसप्ले नहीं मिला जो डिफॉल्ट वैल्यू को एक्सेप्ट करता हो।
अकीरा यमामोटो

मैं एक परिभाषित डिफ़ॉल्ट मान का उपयोग करने वाले 6 अन्य लोगों को गिनता हूं। जाहिरा तौर पर आप यह सब कठिन नहीं लग रहे थे।
सेवार

0

यह संभव नहीं है।
ObjectA.PropertyA.PropertyBविफल हो जाएगा यदि ObjectAअशक्त डेरेफेरिंग के कारण अशक्त है, जो एक त्रुटि है।

if(ObjectA != null && ObjectA.PropertyA... लघु सर्किटिंग के कारण काम करता है, यानी ObjectA.PropertyAअगर जाँच की जा कभी नहीं होगा ObjectAहै null

पहला तरीका जो आप प्रस्तावित करते हैं वह इरादे के साथ सबसे अच्छा और सबसे स्पष्ट है। अगर कुछ भी आप इतने सारे nulls पर भरोसा किए बिना नया स्वरूप देने की कोशिश कर सकते हैं।


-1

एक बार मेमने के बड़े होने पर यह दृष्टिकोण बहुत सीधा होता है:

public static TProperty GetPropertyOrDefault<TObject, TProperty>(this TObject model, Func<TObject, TProperty> valueFunc)  
                                                        where TObject : class
    {
        try
        {
            return valueFunc.Invoke(model);
        }
        catch (NullReferenceException nex)
        {
            return default(TProperty);
        }
    }

उपयोग के साथ ऐसा लग सकता है:

ObjectA objectA = null;

Assert.AreEqual(0,objectA.GetPropertyOrDefault(prop=>prop.ObjectB.ObjectB.ObjectC.ID));

Assert.IsNull(objectA.GetPropertyOrDefault(prop => prop.ObjectB));

बस जिज्ञासु क्यों कोई मुझे प्रतिक्रिया देने के 8 साल बाद नीचा दिखाएगा (जो कि सी # 6 की अशक्त बात से पहले साल था)।
लाठी

-3
var result = nullableproperty ?? defaultvalue;

??(शून्य-वालों ऑपरेटर) का अर्थ है यदि पहला तर्क है null, दूसरा एक के बजाय वापस जाएँ।


2
यह उत्तर ओपी की समस्या को हल नहीं करता है। आप समाधान कैसे लागू करेंगे ?? ObjectA.PropertyA.PropertyB के लिए ऑपरेटर जब अभिव्यक्ति (ObjectA, PropertyA और PropertyB) के सभी हिस्से शून्य हो सकते हैं?
आर्टेमिक्स

सच है, मुझे लगता है कि मैं भी सवाल बिल्कुल नहीं पढ़ा था। वैसे भी, असंभव कुछ भी नहीं है बस यह मत करो: P स्थैतिक शून्य मुख्य (स्ट्रिंग [] args) {a ca = new a (); var default_value = new a () {b = new object ()}; var value = (ca ?? default_value) .b ?? default_value.b; } कक्षा एक {सार्वजनिक वस्तु b = null; }
एरिडेन Álamo

(ObjectA ?? DefaultMockedAtNull) .PropertyA! = Null? ObjectA.PropertyA.PropertyB: null
Aridane
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.