c# - नल कोलेसिंग ऑपरेटर का उपयोग करने के अनोखे तरीके




coding-style null conditional-operator null-coalescing-operator (13)

मैंने इसे आलसी लोड एक-लाइनर के रूप में उपयोग किया है:

public MyClass LazyProp
{
    get { return lazyField ?? (lazyField = new MyClass()); }
}

पठनीय? अपने लिए तय करें।

मुझे पता है कि सी # में नल कोलेसिंग ऑपरेटर का उपयोग करने का मानक तरीका डिफ़ॉल्ट मान सेट करना है।

string nobody = null;
string somebody = "Bob Saget";
string anybody = "";

anybody = nobody   ?? "Mr. T"; // returns Mr. T
anybody = somebody ?? "Mr. T"; // returns "Bob Saget"

लेकिन और क्या कर सकते हैं ?? प्रयोग किया जाता है? यह अधिक संक्षिप्त और पढ़ने के लिए आसान होने के अलावा, टर्नरी ऑपरेटर के रूप में उपयोगी प्रतीत नहीं होता है:

nobody = null;
anybody = nobody == null ? "Bob Saget" : nobody; // returns Bob Saget

तो यह देखते हुए कि कम कोलेरिंग ऑपरेटर के बारे में भी कम पता है ...

  • क्या आपने इस्तेमाल किया है ?? कुछ और के लिए?

  • है ?? जरूरी है, या आप केवल टर्नरी ऑपरेटर का उपयोग करना चाहिए (जो सबसे अधिक परिचित हैं)


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

आपको अपने इरादे को सबसे अच्छा व्यक्त करने का उपयोग करना चाहिए। चूंकि एक नल कोलेसे ऑपरेटर है, इसका इस्तेमाल करें

दूसरी ओर, चूंकि यह बहुत विशिष्ट है, मुझे नहीं लगता कि इसमें अन्य उपयोग हैं। मैं || उचित अधिभार को पसंद करता ऑपरेटर, अन्य भाषाओं के रूप में करते हैं। यह भाषा डिजाइन में अधिक पार्सिमोनियस होगा। लेकिन अच्छा …


विचार करने की एक और बात यह है कि सहकर्मी ऑपरेटर दो बार संपत्ति की प्राप्ति विधि को कॉल नहीं करता है, जैसा कि टर्नरी करता है।

ऐसे में ऐसे परिदृश्य हैं जहां आपको टर्नरी का उपयोग नहीं करना चाहिए, उदाहरण के लिए:

public class A
{
    var count = 0;
    private int? _prop = null;
    public int? Prop
    {
        get 
        {
            ++count;
            return _prop
        }
        set
        {
            _prop = value;
        }
    }
}

यदि तुम प्रयोग करते हो:

var a = new A();
var b = a.Prop == null ? 0 : a.Prop;

गेटर को दो बार बुलाया जाएगा और count चर 2 के बराबर होगा, और यदि आप इसका उपयोग करते हैं:

var b = a.Prop ?? 0

count चर 1 के बराबर होगा, जैसा कि इसे करना चाहिए।


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

असल में, मेरा अनुभव यह है कि बहुत कम लोग टर्नरी ऑपरेटर से परिचित हैं (या अधिक सही ढंग से, सशर्त ऑपरेटर; ?: "टर्नरी" एक ही अर्थ में है कि || बाइनरी है या + या तो यूनरी या बाइनरी है; यह करता है हालांकि कई भाषाओं में एकमात्र टर्नरी ऑपरेटर होता है), इसलिए कम से कम उस सीमित नमूने में, आपका कथन वहां विफल रहता है।

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

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


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

इसका उपयोग करने के लिए, कोड या तो दिखता था

Channel channel;
Authentication authentication;

if (entities == null)
{
    using (entities = Entities.GetEntities())
    {
        channel = entities.GetChannelById(googleShoppingChannelCredential.ChannelId);
        [...]
    }
}
else
{
    channel = entities.GetChannelById(googleShoppingChannelCredential.ChannelId);
    [...]
}

लेकिन एक नल coalesce के साथ बहुत neater हो जाता है

using (entities ?? Entities.GetEntities())
{
    channel = entities.GetChannelById(googleShoppingChannelCredential.ChannelId);
    [...]
}

मैंने उपयोग किया है ?? IDataErrorInfo के कार्यान्वयन में:

public string Error
{
    get
    {
        return this["Name"] ?? this["Address"] ?? this["Phone"];
    }
}

public string this[string columnName]
{
    get { ... }
}

यदि कोई व्यक्तिगत संपत्ति "त्रुटि" स्थिति में है तो मुझे वह त्रुटि मिलती है, अन्यथा मैं शून्य हो जाता हूं। वास्तव में अच्छी तरह से काम करता है।


मैंने इसे दो "थोड़ा विषम" तरीकों से उपयोगी पाया है:

  • TryParse routines लिखते समय out पैरामीटर के विकल्प के रूप में (यानी पार्सिंग विफल होने पर शून्य मान वापस करें)
  • तुलना के लिए "पता नहीं" प्रतिनिधित्व के रूप में

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

public int Compare(Person p1, Person p2)
{
    return PartialComparer.Compare(p1.Age, p2.Age)
        ?? PartialComparer.Compare(p1.Name, p2.Name)
        ?? PartialComparer.Compare(p1.Salary, p2.Salary)
        ?? 0;
}

माना जाता है कि अब मेरे पास कुछ एक्सटेंशन के साथ MiscUtil में ProjectionComparer है, जो इस तरह की चीज़ को और भी आसान बनाता है - लेकिन यह अभी भी साफ है।

समानता लागू करने की शुरुआत में संदर्भ समानता (या शून्यता) की जांच के लिए भी किया जा सकता है।


केवल समस्या यह है कि नल-कोलेसे ऑपरेटर खाली तारों का पता नहीं लगाता है।

अर्थात

string result1 = string.empty ?? "dead code!";

string result2 = null ?? "coalesced!";

उत्पादन:

result1 = ""

result2 = coalesced!

मैं वर्तमान में ओवरराइडिंग में देख रहा हूँ ?? इस के आसपास काम करने के लिए ऑपरेटर। यह फ्रेमवर्क में बनाया गया यह सुनिश्चित करने के लिए आसान होगा।

विचार?


मैं नल coalesce ऑपरेटर आलसी लोड कुछ गुणों के लिए उपयोग करना पसंद है।

मेरे बिंदु को स्पष्ट करने के लिए बस एक बहुत ही सरल (और contrved) उदाहरण:

public class 
{
    private IEnumerable<string> _definitions;
    public IEnumerable<string> Definitions
    {
        get
        {
            return _definitions ?? (
                _definitions = new List<string>
                {
                    "definition 1",
                    "definition 2",
                    "definition 3"
                }
            );
        }
    } 
}

ठंडा! मुझे किसी ऐसे व्यक्ति के रूप में गिनें जो नल कोलेसिंग ऑपरेटर के बारे में नहीं जानता - यह सुंदर निफ्टी सामान है।

मुझे टर्नरी ऑपरेटर की तुलना में पढ़ने में बहुत आसान लगता है।

पहली जगह जो दिमाग में आती है, जहां मैं इसका उपयोग कर सकता हूं वह है कि मेरे सभी डिफ़ॉल्ट पैरामीटर एक ही स्थान पर रखें।

public void someMethod( object parm2, ArrayList parm3 )
{ 
  someMethod( null, parm2, parm3 );
}
public void someMethod( string parm1, ArrayList parm3 )
{
  someMethod( parm1, null, parm3 );
}
public void someMethod( string parm1, object parm2, )
{
  someMethod( parm1, parm2, null );
}
public void someMethod( string parm1 )
{
  someMethod( parm1, null, null );
}
public void someMethod( object parm2 )
{
  someMethod( null, parm2, null );
}
public void someMethod( ArrayList parm3 )
{
  someMethod( null, null, parm3 );
}
public void someMethod( string parm1, object parm2, ArrayList parm3 )
{
  // Set your default parameters here rather than scattered through the above function overloads
  parm1 = parm1 ?? "Default User Name";
  parm2 = parm2 ?? GetCurrentUserObj();
  parm3 = parm3 ?? DefaultCustomerList;

  // Do the rest of the stuff here
}

एक बात जो मैं हाल ही में कर रहा हूं बैकअप के लिए नल कोलेसिंग का उपयोग कर रहा हूं। उदाहरण के लिए:

object boxed = 4;
int i = (boxed as int?) ?? 99;

Console.WriteLine(i); // Prints 4

यह लंबी श्रृंखलाओं का बैक अप लेने के लिए भी उपयोगी है ?. जो प्रत्येक विफल हो सकता है

int result = MyObj?.Prop?.Foo?.Val ?? 4;
string other = (MyObj?.Prop?.Foo?.Name as string)?.ToLower() ?? "not there";

एक और फायदा यह है कि टर्नरी ऑपरेटर को दोहरे मूल्यांकन या अस्थायी चर की आवश्यकता होती है।

उदाहरण के लिए, इस पर विचार करें:

string result = MyMethod() ?? "default value";

जबकि टर्नरी ऑपरेटर के साथ आप या तो छोड़ दिया जाता है:

string result = (MyMethod () != null ? MyMethod () : "default value");

जो MyMethod को दो बार कॉल करता है, या:

string methodResult = MyMethod ();
string result = (methodResult != null ? methodResult : "default value");

किसी भी तरह से, नल कोलेसिंग ऑपरेटर क्लीनर है और, मुझे लगता है, अधिक कुशल।


मैं एक सी # विशेषज्ञ नहीं हूं जैसा कि आप मेरे प्रश्न इतिहास से देख सकते हैं, लेकिन, मैंने इसे आजमाया और मुझे लगता है कि यह एक बग है .... लेकिन एक नौसिखिया के रूप में, मुझे यह कहना है कि मुझे सबकुछ समझ नहीं आता यहां पर अगर मैं रास्ता बंद कर दूंगा तो मैं अपना जवाब हटा दूंगा।

मैं आपके कार्यक्रम का एक अलग संस्करण बनाकर इस bug निष्कर्ष पर आया हूं जो एक ही परिदृश्य से संबंधित है, लेकिन बहुत कम जटिल है।

मैं बैकिंग स्टोर्स के साथ तीन शून्य पूर्णांक गुणों का उपयोग कर रहा हूं। मैं प्रत्येक को 4 सेट करता हूं और फिर int? something2 = (A ?? B) ?? C; चलाता हूं int? something2 = (A ?? B) ?? C; int? something2 = (A ?? B) ?? C;

( यहां पूरा कोड )

यह सिर्फ ए पढ़ता है और कुछ भी नहीं।

मेरे लिए यह बयान मेरे जैसा दिखता है:

  1. ब्रैकेट में प्रारंभ करें, ए देखें, ए को वापस करें और समाप्त करें यदि कोई शून्य नहीं है।
  2. अगर ए शून्य था, बी का मूल्यांकन करें, अगर बी शून्य नहीं है तो खत्म करें
  3. यदि ए और बी शून्य थे, तो सी का मूल्यांकन करें।

इसलिए, चूंकि ए शून्य नहीं है, यह केवल ए और खत्म होता है।

आपके उदाहरण में, फर्स्ट केस में ब्रेकपॉइंट डालने से पता चलता है कि एक्स, वाई और जेड सभी शून्य नहीं हैं और इसलिए, मैं उनसे अपेक्षा करता हूं कि वे मेरे कम जटिल उदाहरण के समान व्यवहार करें .... लेकिन मुझे डर है कि मैं बहुत अधिक हूं एक सी # नौसिखिया के और इस सवाल के बिंदु को पूरी तरह याद किया है!







c# coding-style null conditional-operator null-coalescing-operator