नल Coalescing ऑपरेटर का उपयोग करने के लिए अनोखे तरीके [बंद]


164

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

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

तो यह देखते हुए कि कम से कम अशक्त सहकर्मी ऑपरेटर के बारे में पता है ...

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

  • है ??आवश्यक हो, या तुम सिर्फ त्रिगुट ऑपरेटर का उपयोग करना चाहिए (कि ज्यादातर से परिचित हैं)

जवाबों:


216

खैर, सबसे पहले, यह मानक टर्नरी की तुलना में श्रृंखला के लिए बहुत आसान है:

string anybody = parm1 ?? localDefault ?? globalDefault;

बनाम

string anyboby = (parm1 != null) ? parm1 
               : ((localDefault != null) ? localDefault 
               : globalDefault);

यह भी अच्छी तरह से काम करता है अगर अशक्त संभव वस्तु एक चर नहीं है:

string anybody = Parameters["Name"] 
              ?? Settings["Name"] 
              ?? GlobalSetting["Name"];

बनाम

string anybody = (Parameters["Name"] != null ? Parameters["Name"] 
                 : (Settings["Name"] != null) ? Settings["Name"]
                 :  GlobalSetting["Name"];

12
श्रृंखलन, ऑपरेटर के लिए एक बड़ा प्लस है अनावश्यक आईएफएस का एक समूह को हटा
chakrit

1
मैंने अभी इसका उपयोग एक साधारण IF ब्लॉक को बदलने के लिए किया था, जो मैंने पहले लिखा था कि मुझे या तो टर्नेरी या नल कोलेसिंग ऑपरेटर के बारे में पता था। मूल IF स्टेटमेंट की सही और गलत शाखाओं को एक ही विधि कहा जाता है, यदि एक निश्चित इनपुट NULL है, तो इसके विभिन्न तर्कों के साथ इसके एक तर्क को प्रतिस्थापित करें। अशक्त सहवर्ती ऑपरेटर के साथ, यह एक कॉल है। यह वास्तव में शक्तिशाली है जब आपके पास एक ऐसी विधि है जिसमें दो या अधिक ऐसे प्रतिस्थापन की आवश्यकता होती है!
डेविड ए। ग्रे

177

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

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

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


6
हम्म्म, आपने एक प्रतिवाद पाया "क्यों कोई इसे एक ओफ़्फ़ुसेटेड आईएफ के रूप में उपयोग करना चाहेगा" ... जो वास्तव में मेरे लिए बहुत पठनीय है।
गोडके

6
मुझे कुछ याद आ रहा है (मैं ज्यादातर जावा का उपयोग करता हूं), लेकिन क्या वहां कोई दौड़ की स्थिति नहीं है?
जस्टिन के

9
@ जस्टिन के - केवल एक दौड़ की स्थिति है अगर कई धागे एक ही वस्तु के लेज़ीप्रॉप संपत्ति तक पहुंच रहे हैं। यह आसानी से एक ताला के साथ तय करने योग्य है, अगर प्रत्येक उदाहरण के थ्रेड सेफ्टी की आवश्यकता होती है। स्पष्ट रूप से इस उदाहरण में, इसकी आवश्यकता नहीं है।
जेफरी एल व्हाइटलेज

5
@ जेफ्री: यदि यह स्पष्ट था, तो मैंने सवाल नहीं पूछा होगा। :) जब मैंने उस उदाहरण को देखा, तो मैंने तुरंत एक सिंगलटन सदस्य के बारे में सोचा, और जब से मैं एक मल्टीथ्रेडेड वातावरण में अपनी अधिकांश कोडिंग करता हूं ... लेकिन, हाँ, अगर हम मानते हैं कि कोड सही है, तो कुछ भी अतिरिक्त अनावश्यक है।
जस्टिन के

7
यह दौड़ की स्थिति के लिए एक सिंगलटन होना जरूरी नहीं है। क्लास का सिर्फ एक साझा उदाहरण जिसमें LazyProp और कई थ्रेड शामिल हैं जो LazyProp तक पहुंचते हैं। Lazy <T> इस तरह का काम करने का एक बेहतर तरीका है, और डिफ़ॉल्ट रूप से थ्रेडसेफ़ है (आप Lazy <T> के थ्रेडसेफ़्टी को मॉडिफाई करने का चुनाव कर सकते हैं)।
नियाल कनॉटघटन

52

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

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

बाद वाले को थोड़ी और जानकारी चाहिए। आमतौर पर जब आप कई तत्वों के साथ तुलना करते हैं, तो आपको यह देखना होगा कि तुलना का पहला भाग (जैसे उम्र) एक निश्चित उत्तर देता है, तो अगला भाग (उदाहरण के लिए नाम) केवल तभी यदि पहला भाग मदद नहीं करता है। अशक्त coalescing ऑपरेटर का उपयोग करने का मतलब है कि आप बहुत सरल तुलना लिख ​​सकते हैं (चाहे आदेश या समानता के लिए)। उदाहरण के लिए, 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 है, जो इस तरह की चीज़ को और भी आसान बनाते हैं - लेकिन यह अभी भी साफ है।

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


मुझे यह पसंद है कि आपने PartialComparer के साथ क्या किया है, लेकिन उन मामलों की तलाश में था जहां मुझे मूल्यांकन अभिव्यक्ति चर रखने की आवश्यकता है। मैं लंबोदर और एक्सटेंशन में पारंगत नहीं हूं, तो क्या आप देख सकते हैं कि निम्नलिखित समान पैटर्न का पालन करता है (यानी यह काम नहीं करता है)? stackoverflow.com/questions/1234263/#1241780
मैक्सवेल

33

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

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

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

आपके साथ छोड़े गए परिचालक के साथ:

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

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

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

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


1
+1। यह एक बड़ा कारण है कि मुझे अशक्त सहवर्ती ऑपरेटर पसंद है। MyMethod()किसी भी प्रकार के साइड इफेक्ट होने पर कॉल करना विशेष रूप से उपयोगी है ।
बजे एक CVn

यदि MyMethod()किसी मूल्य को वापस करने के बाहर कोई प्रभाव नहीं पड़ता है, तो कंपाइलर इसे दो बार कॉल नहीं करना जानता है, इसलिए आपको अधिकांश मामलों में यहां दक्षता के बारे में चिंता करने की आवश्यकता नहीं है।
टाइनीटीमज़ामोनी

MyMethod()बिंदीदार वस्तुओं के जंजीर अनुक्रम होने पर यह चीजों को अधिक पठनीय IMHO भी रखता है । Ex:myObject.getThing().getSecondThing().getThirdThing()
xdhmoore

@TinyTimZamboni, क्या आपके पास संकलक के इस व्यवहार का संदर्भ है?
कुबा व्येरोसेक

@ क्यूबाविकॉस्टेक मुझे C # संकलक के विशिष्ट कामकाज का ज्ञान नहीं है, लेकिन मुझे llvm के साथ स्थिर संकलक सिद्धांत के साथ कुछ अनुभव है। इस तरह एक कॉल का अनुकूलन करने के लिए एक संकलक ले जा सकता है दृष्टिकोण के एक नंबर है। ग्लोबल वैल्यू नंबरिंग ध्यान देगी कि MyMethodइस संदर्भ में दो कॉल समान हैं, यह मानते हुए कि MyMethodयह एक शुद्ध कार्य है। एक अन्य विकल्प स्वचालित संस्मरण है या केवल फ़ंक्शन कैश में बंद है। दूसरी ओर: en.wikipedia.org/wiki/Global_value_numbering
TinyTimZamboni

23

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

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

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 करने के लिए बराबर हो जाएगा।


4
यह अधिक उत्थान के योग्य है। मैंने कई बार sooo पढ़ा है जो ??इसके बराबर है ?:
कुबा व्येरोसेक

1
एक दो बार बुलाया जा रहा है के बारे में वैध बिंदु। लेकिन इस उदाहरण मैं एक खराब डिजाइन संरक्षक को इस तरह के भ्रामक रूप से नामित करने वाला माना जाएगा जो वास्तव में वस्तु में परिवर्तन कर सकता है।
लिनस

15

??ऑपरेटर को मिलने वाला सबसे बड़ा फायदा यह है कि आप आसानी से अशक्त मूल्य प्रकारों को गैर-अशक्त प्रकारों में परिवर्तित कर सकते हैं:

int? test = null;
var result = test ?? 0; // result is int, not int?

मैं अक्सर Linq प्रश्नों में इसका उपयोग करता हूं:

Dictionary<int, int?> PurchaseQuantities;
// PurchaseQuantities populated via ASP .NET MVC form.
var totalPurchased = PurchaseQuantities.Sum(kvp => kvp.Value ?? 0);
// totalPurchased is int, not int?

मैं यहाँ थोड़ा लेट हो सकता हूँ, लेकिन यह दूसरा उदाहरण होगा kvp == null। और वास्तव Nullable<T>में एक GetValueOrDefaultविधि है जो मैं सामान्य रूप से उपयोग करता हूं।
कॉम्पबुक

6
KeyValuePair .NET फ्रेमवर्क में एक वैल्यू टाइप है, इसलिए इसके किसी भी गुण तक पहुँच कभी भी एक शून्य संदर्भ अपवाद नहीं होगी। msdn.microsoft.com/en-us/library/5tbh8a42(v=vs.110).aspx
Ryan

9

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

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

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

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


दिलचस्प। आप "इसे" एक संपत्ति के रूप में उपयोग कर रहे हैं। मैंने ऐसा कभी नहीं किया है।
आर्मस्ट्रांगेस्ट

हाँ, यह IDataErrorInfo कैसे काम करता है का हिस्सा है। आम तौर पर वह वाक्य रचना केवल संग्रह कक्षाओं पर उपयोगी होती है।
मैट हैमिल्टन

4
आप में त्रुटि संदेश भंडारण कर रहे हैं this["Name"], this["Address"], आदि ??
एंड्रयू

7

आप एक वैकल्पिक पैरामीटर सेट नहीं होने वाले मामले को संभालने के लिए इसे थोड़ा सा क्लीनर बनाने के लिए नल कोलेसिंग ऑपरेटर का उपयोग कर सकते हैं:

public void Method(Arg arg = null)
{
    arg = arg ?? Arg.Default;
    ...

क्या यह बहुत अच्छा नहीं होगा अगर इस लाइन को लिखा जा सके arg ?= Arg.Default?
जेसी डे विट

6

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

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

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

Resharper वास्तव में इसे "पारंपरिक" आलसी लोड के लिए एक रिफ्लेक्टर के रूप में सुझाएगा।
arichards

5

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

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

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

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


मेरी ओर से बहुत देर से टिप्पणी - लेकिन किसी को कवर करते हुए देखकर खुशी हुई कि एक टर्नरी ऑपरेटर एक ऑपरेटर है जिसके तीन तर्क हैं (जिनमें से अब C # में एक से अधिक हैं)।
स्टीव किड

5

एक चीज जो मैं बहुत समय से कर रहा हूं वह बैकअप के लिए अशक्त सहूलियत का उपयोग कर रही है as। उदाहरण के लिए:

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";

4

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


अर्थात

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

string result2 = null ?? "coalesced!";

उत्पादन:

result1 = ""

result2 = coalesced!

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

विचार?


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

हाँ, यह एक लगातार परिदृश्य है ... यहां तक ​​कि एक विशेष विधि है String.IsNullOrEmpty (स्ट्रिंग) ...
मैक्स गालिन

12
"अशक्त-मोटे संचालक खाली तारों का पता नहीं लगाते हैं।" खैर यह है अशक्त -coalescing ऑपरेटर, नहीं nullOrEmpty -coalescing ऑपरेटर। और व्यक्तिगत रूप से, मैं दोनों के बीच अंतर करने वाली भाषाओं में अशक्त और खाली मूल्यों का मिश्रण करने से घृणा करता हूं, जो उन चीजों के साथ अंतर बनाता है जो किसी भी कष्टप्रद नहीं हैं। और मैं थोड़ा जुनूनी-बाध्यकारी हूं, इसलिए यह मुझे परेशान करता है जब भाषाएं / कार्यान्वयन दोनों में किसी भी तरह से अंतर नहीं करते हैं, भले ही मैं तर्क क्यों समझूं (जैसे [के अधिकांश कार्यान्वयन?] SQL)।
JAB

3
??अतिभारित नहीं किया जा सकता है: msdn.microsoft.com/en-us/library/8edha89s(v=vs.100).aspx - हालांकि अतिभारित होने के लिए यह एक शानदार होगा। मैं एक संयोजन का उपयोग करता हूं: s1.Nullify() ?? s2.Nullify()जहां स्ट्रिंग खाली होने पर मामलों में string Nullify(this s)रिटर्न देता nullहै।
किट

एकमात्र समस्या? मैंने सिर्फ खुद को चाहा है ?? = और यह पाया कि यह करने का कोई तरीका था या नहीं (स्थिति: पहला पास अपवाद मामलों को लोड करता है, अब मैं कुछ भी पहले से लोड नहीं होने वाले डिफ़ॉल्ट मूल्यों के माध्यम से वापस जाना चाहता हूं।)
लोरेन Pechtel

3

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

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

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


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
}

2

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

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

Channel channel;
Authentication authentication;

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

लेकिन एक अशक्त मोटे आदमी के साथ ज्यादा भद्दा हो जाता है

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

0

मैंने इस तरह का उपयोग किया है:

for (int i = 0; i < result.Count; i++)
            {
                object[] atom = result[i];

                atom[3] = atom[3] ?? 0;
                atom[4] = atom[4] != null ? "Test" : string.Empty;
                atom[5] = atom[5] ?? "";
                atom[6] = atom[6] ?? "";
                atom[7] = atom[7] ?? "";
                atom[8] = atom[8] ?? "";
                atom[9] = atom[9] ?? "";
                atom[10] = atom[10] ?? "";
                atom[12] = atom[12] ?? false; 
            }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.