अशक्त मूल्यों की जाँच करने का उचित तरीका क्या है?


122

मुझे null-coalescing ऑपरेटर से प्यार है क्योंकि यह अशक्त प्रकारों के लिए डिफ़ॉल्ट मान असाइन करना आसान बनाता है।

 int y = x ?? -1;

यह बहुत अच्छा है, सिवाय इसके कि मुझे कुछ सरल करने की आवश्यकता है x। उदाहरण के लिए, अगर मैं जांच करना चाहता हूं Session, तो मैं आमतौर पर कुछ और क्रिया लिखना चाहता हूं।

काश मैं ऐसा कर पाता:

string y = Session["key"].ToString() ?? "none";

लेकिन आप नहीं कर सकते क्योंकि .ToString()अशक्त जाँच से पहले बुलाया जाता है तो यह विफल रहता है यदि Session["key"]अशक्त है। मैं यह कर रहा हूं:

string y = Session["key"] == null ? "none" : Session["key"].ToString();

यह काम करता है और बेहतर है, मेरी राय में, तीन-लाइन विकल्प की तुलना में:

string y = "none";
if (Session["key"] != null)
    y = Session["key"].ToString();

हालांकि यह काम करता है कि मैं अभी भी उत्सुक हूं अगर कोई बेहतर तरीका है। यह कोई फर्क नहीं पड़ता कि मुझे हमेशा Session["key"]दो बार क्या कहना है ; एक बार जांच के लिए, और फिर असाइनमेंट के लिए। कोई विचार?


20
यह तब है जब मैं चाहता हूं कि C # में एक "सुरक्षित नेविगेशन ऑपरेटर" ( .?) हो, जैसे ग्रूवी के पास है
कैमरन

2
@Cameron: यह तब होता है जब मैं चाहता हूं कि C # एक भिक्षु के रूप में अशक्त प्रकारों (संदर्भ प्रकारों सहित) का इलाज कर सके, इसलिए आपको "सुरक्षित नेविगेशन ऑपरेटर" की आवश्यकता नहीं होगी।
जॉन प्यूरी

3
अशक्त संदर्भों के आविष्कारक ने इसे अपनी "अरब डॉलर की गलती" कहा और मैं सहमत हूं। देखिए infoq.com/pretations/…
Jamie Ide

उसकी वास्तविक गलती अशक्त (गैर-भाषा नहीं) असुरक्षित और गैर-नलबेल प्रकारों का मिश्रण है।
MSalters

@JamieIde एक बहुत ही रोचक लिंक के लिए धन्यवाद। :)
BobRodes

जवाबों:


182

व्हाट अबाउट

string y = (Session["key"] ?? "none").ToString();

79
इसके कारण सेना मजबूत है।
चेव

2
@ मैथ्यू: नहीं क्योंकि सत्र मान प्रकार के होते हैं
ब्लैकबियर

1
@BlackBear लेकिन वापस लौटाया गया मान शायद एक स्ट्रिंग है, इसलिए कलाकारों को मान्य है
Firo

यह मेरे सवाल का सबसे सीधा जवाब था इसलिए मैं जवाब को चिह्नित कर रहा हूं, लेकिन जॉन स्कीट की विस्तार पद्धति .ToStringOrDefault()मेरे काम करने का पसंदीदा तरीका है। हालाँकि, मैं जॉन के विस्तार विधि के भीतर इस उत्तर का उपयोग कर रहा हूं;)
Chev

10
मुझे यह नापसंद है क्योंकि यदि आपके पास सत्र में कोई अन्य प्रकार की वस्तु है जो आप अपेक्षा करते हैं तो आप अपने कार्यक्रम में कुछ सूक्ष्म कीड़े छिपा सकते हैं। मैं इसके बजाय एक सुरक्षित कास्ट का उपयोग करना चाहता हूं क्योंकि मुझे लगता है कि यह तेजी से त्रुटियों की संभावना है। यह एक स्ट्रिंग ऑब्जेक्ट पर ToString () को कॉल करने से भी बचता है।
tvanfosson

130

यदि आप अक्सर ऐसा विशेष रूप सेToString() कर रहे हैं तो आप एक एक्सटेंशन विधि लिख सकते हैं:

public static string NullPreservingToString(this object input)
{
    return input == null ? null : input.ToString();
}

...

string y = Session["key"].NullPreservingToString() ?? "none";

या निश्चित रूप से डिफ़ॉल्ट लेने की एक विधि:

public static string ToStringOrDefault(this object input, string defaultValue)
{
    return input == null ? defaultValue : input.ToString();
}

...

string y = Session["key"].ToStringOrDefault("none");

16
.ToStringOrDefault()सरल और ग्यारह है। एक अच्छा समाधान।
चेव

7
मैं इससे बिल्कुल सहमत नहीं हो सकता। एक्सटेंशन मेथड्स objectएक अभिशाप हैं और कोड बेस को जंक करते हैं, और अशक्त thisमूल्यों पर त्रुटि के बिना काम करने वाले एक्सटेंशन तरीके शुद्ध बुराई हैं।
निक लार्सन

10
@NickLarsen: मॉडरेशन में सब कुछ, मैं कहता हूं। एक्सटेंशन विधियां जो अशक्त के साथ काम करती हैं, बहुत उपयोगी हो सकती हैं, IMO - जब तक वे क्या करते हैं, इसके बारे में स्पष्ट हैं।
जॉन स्कीट

3
@ one.beat.consumer: हाँ। यदि यह सिर्फ स्वरूपण (या कोई टाइपो) होता, तो यह एक बात होती, लेकिन लेखक की विधि का नाम बदलना सामान्य रूप से उपयुक्त संपादन, आईएमओ से परे है।
जॉन स्कीट

6
@ one.beat.consumer: जब व्याकरण और टाइपो को ठीक करते हैं, तो यह ठीक है - लेकिन एक ऐसा नाम जिसे कोई (कोई भी, केवल मुझे नहीं) बदलकर स्पष्ट रूप से जानबूझकर चुना गया है, मुझे अलग लगता है। उस बिंदु पर, मैं इसके बजाय एक टिप्पणी में सुझाव देना चाहूंगा।
जॉन स्कीट

21

आप यह भी उपयोग कर सकते हैं as, जो nullरूपांतरण विफल होने पर उपज देता है:

Session["key"] as string ?? "none"

यह वापसी होगी "none", भले ही किसी को एक भरवां intमें Session["key"]


1
यह केवल तभी काम करता है जब आपको ToString()पहली जगह की आवश्यकता नहीं होगी ।
हाबिल

1
मुझे आश्चर्य है कि अभी तक किसी ने भी इस उत्तर को अस्वीकार नहीं किया है। यह शब्दार्थ ओपी क्या करना चाहता है, उससे पूरी तरह अलग है।
तिमवी

@ टिमिवी: ओपी ToString()एक स्ट्रिंग से युक्त ऑब्जेक्ट को स्ट्रिंग में बदलने के लिए उपयोग करता है। आप के साथ obj as stringया कर सकते हैं (string)obj। यह ASP.NET में काफी सामान्य स्थिति है।
एंडोमर

5
@Andomar: नहीं, ओपी ToString()एक ऑब्जेक्ट (अर्थात् Session["key"]) पर कॉल कर रहा है , जिसका प्रकार उसने उल्लेख नहीं किया है। यह किसी भी प्रकार की वस्तु हो सकती है, जरूरी नहीं कि यह एक तार हो।
तिमवी

13

यदि यह हमेशा एक होगा string, तो आप कास्ट कर सकते हैं:

string y = (string)Session["key"] ?? "none";

यह गलती को छुपाने के बजाय शिकायत करने का लाभ है यदि कोई व्यक्ति किसी intचीज को भरता है Session["key"]। ;)


10

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

एक साधारण जेनेरिक एक्सटेंशन विधि जो एक स्ट्रिंग को एक कुंजी के रूप में स्वीकार करती है, और फिर सत्र ऑब्जेक्ट को टाइप करती है। यदि ऑब्जेक्ट शून्य है या समान प्रकार नहीं है, तो डिफ़ॉल्ट लौटा दिया जाता है, अन्यथा सत्र मान को दृढ़ता से टाइप किया जाता है।

कुछ इस तरह

/// <summary>
/// Gets a value from the current session, if the type is correct and present
/// </summary>
/// <param name="key">The session key</param>
/// <param name="defaultValue">The default value</param>
/// <returns>Returns a strongly typed session object, or default value</returns>
public static T GetValueOrDefault<T>(this HttpSessionState source, string key, T defaultValue)
{
    // check if the session object exists, and is of the correct type
    object value = source[key]
    if (value == null || !(value is T))
    {
        return defaultValue;
    }

    // return the session object
    return (T)value;
}

1
क्या आप इस विस्तार विधि के लिए एक उपयोग नमूना शामिल कर सकते हैं? स्टेटबाग व्यू स्टेट के साथ डील नहीं करता है और सेशन नहीं? मैं ASP.NET MVC 3 का उपयोग कर रहा हूं, इसलिए मुझे वास्तव में राज्य को देखने की सरल पहुंच नहीं है। मुझे लगता है कि आप विस्तार करना चाहते हैं HttpSessionState
Chev

यदि यह सफल होता है, तो इस उत्तर को मूल्य 3x और 2 कास्ट प्राप्त करने की आवश्यकता होती है। (मुझे पता है कि यह एक शब्दकोश है, लेकिन शुरुआती महंगे तरीकों पर समान प्रथाओं का उपयोग कर सकते हैं।)
जेक बर्गर

3
T value = source[key] as T; return value ?? defaultValue;
जेक बर्गर

1
@jber "के रूप में" का उपयोग करने के लिए कास्टिंग बहुत ही दुर्गम है क्योंकि सामान्य प्रकार पर कोई वर्ग बाधा नहीं है क्योंकि संभवतः आप एक मूल्य जैसे कि बूल वापस करना चाहते हैं। @AlexFord मेरी क्षमायाचना, आप HttpSessionStateसत्र के लिए विस्तार करना चाहते हैं । :)
रिचर्ड

वास्तव में। के रूप में ध्यान दिया, बाधा की आवश्यकता है। (... और एक अन्य विधि यदि आप मूल्य प्रकारों का उपयोग करना चाहते हैं)
जेक बर्गर

7

हम एक विधि का उपयोग करते हैं NullOr

प्रयोग

// Call ToString() if it’s not null, otherwise return null
var str = myObj.NullOr(obj => obj.ToString());

// Supply default value for when it’s null
var str = myObj.NullOr(obj => obj.ToString()) ?? "none";

// Works with nullable return values, too —
// this is properly typed as “int?” (nullable int)
// even if “Count” is just int
var count = myCollection.NullOr(coll => coll.Count);

// Works with nullable input types, too
int? unsure = 47;
var sure = unsure.NullOr(i => i.ToString());

स्रोत

/// <summary>Provides a function delegate that accepts only value types as return types.</summary>
/// <remarks>This type was introduced to make <see cref="ObjectExtensions.NullOr{TInput,TResult}(TInput,FuncStruct{TInput,TResult})"/>
/// work without clashing with <see cref="ObjectExtensions.NullOr{TInput,TResult}(TInput,FuncClass{TInput,TResult})"/>.</remarks>
public delegate TResult FuncStruct<in TInput, TResult>(TInput input) where TResult : struct;

/// <summary>Provides a function delegate that accepts only reference types as return types.</summary>
/// <remarks>This type was introduced to make <see cref="ObjectExtensions.NullOr{TInput,TResult}(TInput,FuncClass{TInput,TResult})"/>
/// work without clashing with <see cref="ObjectExtensions.NullOr{TInput,TResult}(TInput,FuncStruct{TInput,TResult})"/>.</remarks>
public delegate TResult FuncClass<in TInput, TResult>(TInput input) where TResult : class;

/// <summary>Provides extension methods that apply to all types.</summary>
public static class ObjectExtensions
{
    /// <summary>Returns null if the input is null, otherwise the result of the specified lambda when applied to the input.</summary>
    /// <typeparam name="TInput">Type of the input value.</typeparam>
    /// <typeparam name="TResult">Type of the result from the lambda.</typeparam>
    /// <param name="input">Input value to check for null.</param>
    /// <param name="lambda">Function to apply the input value to if it is not null.</param>
    public static TResult NullOr<TInput, TResult>(this TInput input, FuncClass<TInput, TResult> lambda) where TResult : class
    {
        return input == null ? null : lambda(input);
    }

    /// <summary>Returns null if the input is null, otherwise the result of the specified lambda when applied to the input.</summary>
    /// <typeparam name="TInput">Type of the input value.</typeparam>
    /// <typeparam name="TResult">Type of the result from the lambda.</typeparam>
    /// <param name="input">Input value to check for null.</param>
    /// <param name="lambda">Function to apply the input value to if it is not null.</param>
    public static TResult? NullOr<TInput, TResult>(this TInput input, Func<TInput, TResult?> lambda) where TResult : struct
    {
        return input == null ? null : lambda(input);
    }

    /// <summary>Returns null if the input is null, otherwise the result of the specified lambda when applied to the input.</summary>
    /// <typeparam name="TInput">Type of the input value.</typeparam>
    /// <typeparam name="TResult">Type of the result from the lambda.</typeparam>
    /// <param name="input">Input value to check for null.</param>
    /// <param name="lambda">Function to apply the input value to if it is not null.</param>
    public static TResult? NullOr<TInput, TResult>(this TInput input, FuncStruct<TInput, TResult> lambda) where TResult : struct
    {
        return input == null ? null : lambda(input).Nullable();
    }
}

हां, यह उस समस्या के लिए अधिक सामान्य उत्तर है - जो आपने मुझे हराया है - और सुरक्षित नेविगेशन के लिए एक उम्मीदवार (यदि आप साधारण चीजों के लिए लैम्ब्डा-एस का बुरा नहीं मानते हैं) - लेकिन यह अभी भी लिखने के लिए थोड़ा बोझिल है, कुंआ :)। व्यक्तिगत रूप से मैं हमेशा चुनूंगा? : इसके बजाय (यदि महंगा नहीं है, अगर यह वैसे भी फिर से व्यवस्थित है) ...
NSGaga- ज्यादातर-निष्क्रिय

... और 'नामकरण' इस एक के साथ वास्तविक समस्या है - कुछ भी वास्तव में सही चित्रण करने के लिए नहीं लगता है (या 'बहुत अधिक' जोड़ता है), या लंबा है - NullOr अच्छा है, लेकिन 'null' IMO पर अधिक जोर (प्लस आप है? अभी भी) - 'संपत्ति', या 'सुरक्षित' है जो मैंने इस्तेमाल किया। value.Dot (o => o.property) ?? @ डेफॉल्ट शायद?
NSGaga- ज्यादातर-निष्क्रिय-

@NSGaga: हम कुछ समय के लिए नाम पर आगे और पीछे चले गए। हमने विचार किया Dotलेकिन यह बहुत ही अवांछनीय पाया। हम NullOrआत्म-स्पष्टीकरण और संक्षिप्तता के बीच एक अच्छे व्यापार के रूप में बस गए । यदि आपको वास्तव में नामकरण की परवाह नहीं है, तो आप हमेशा इसे कॉल कर सकते हैं _। यदि आपको लिखने के लिए लंबोदर बहुत बोझिल लगते हैं, तो आप इसके लिए एक स्निपेट का उपयोग कर सकते हैं, लेकिन व्यक्तिगत रूप से मुझे यह बहुत आसान लगता है। जैसा कि ? :, आप अधिक जटिल अभिव्यक्तियों के साथ उपयोग नहीं कर सकते, आपको उन्हें एक नए स्थानीय में ले जाना होगा; NullOrआपको इससे बचने की अनुमति देता है।
टिम्मवी

6

मेरी प्राथमिकता, एक बंद के लिए, कुंजी के साथ संग्रहीत वस्तु के मामले में स्ट्रिंग के लिए एक सुरक्षित कास्ट का उपयोग करना होगा। उपयोग करने के ToString()लिए आपके इच्छित परिणाम नहीं हो सकते हैं।

var y = Session["key"] as string ?? "none";

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

public class SessionWrapper
{
    private HttpSessionBase Session { get; set; }

    public SessionWrapper( HttpSessionBase session )
    {
        Session = session;
    }

    public SessionWrapper() : this( HttpContext.Current.Session ) { }

    public string Key
    {
         get { return Session["key"] as string ?? "none";
    }

    public int MaxAllowed
    {
         get { return Session["maxAllowed"] as int? ?? 10 }
    }
}

इसके समान इस्तेमाल किया

 var session = new SessionWrapper(Session);

 string key = session.Key;
 int maxAllowed = session.maxAllowed;

3

एक सहायक समारोह बनाएँ

public static String GetValue( string key, string default )
{
    if ( Session[ key ] == null ) { return default; }
    return Session[ key ].toString();
}


string y = GetValue( 'key', 'none' );

2

स्कीट का जवाब सबसे अच्छा है - विशेष रूप से मुझे लगता है कि उनकी ToStringOrNull()उपस्थिति काफी सुंदर है और आपकी जरूरत के हिसाब से सबसे उपयुक्त है। मैं विस्तार विधियों की सूची में एक और विकल्प जोड़ना चाहता था:

मूल ऑब्जेक्ट या मूल स्ट्रिंग मान को शून्य के लिए लौटाएँ :

// Method:
public static object OrNullAsString(this object input, string defaultValue)
{
    if (defaultValue == null)
        throw new ArgumentNullException("defaultValue");
    return input == null ? defaultValue : input;
}

// Example:
var y = Session["key"].OrNullAsString("defaultValue");

varलौटे मूल्य के लिए उपयोग करें क्योंकि यह मूल इनपुट के प्रकार के रूप में वापस आएगा, केवल डिफ़ॉल्ट स्ट्रिंग के रूप में जबnull


null defaultValueयदि इसकी आवश्यकता नहीं है (तो यह) पर एक अपवाद क्यों फेंकें input != null?
अत्तिला

एक input != nullवस्तु अपने आप ही वस्तु को लौटा देगी। input == nullएक पैराम के रूप में प्रदान की गई स्ट्रिंग लौटाता है। इसलिए यह संभव है कि कोई व्यक्ति कॉल कर सकता है .OnNullAsString(null)- लेकिन उद्देश्य (शायद ही कभी उपयोगी विस्तार विधि) आपको यह सुनिश्चित करने के लिए था कि या तो वस्तु वापस मिल जाए या डिफ़ॉल्ट स्ट्रिंग ... कभी भी अशक्त न हो
one.beat.consumer

input!=nullपरिदृश्य केवल इनपुट वापस आ जाएगी, तो defaultValue!=nullभी रखती है; अन्यथा यह एक फेंक देते हैं ArgumentNullException
अत्तिला

0

यह .NET के संस्करणों के लिए मेरा छोटा प्रकार सुरक्षित "एल्विस ऑपरेटर" है जो समर्थन नहीं करता है।

public class IsNull
{
    public static O Substitute<I,O>(I obj, Func<I,O> fn, O nullValue=default(O))
    {
        if (obj == null)
            return nullValue;
        else
            return fn(obj);
    }
}

पहला तर्क परीक्षित वस्तु है। दूसरा है फंक्शन। और तीसरा शून्य मान है। तो आपके मामले के लिए:

IsNull.Substitute(Session["key"],s=>s.ToString(),"none");

यह अशक्त प्रकारों के लिए भी बहुत उपयोगी है। उदाहरण के लिए:

decimal? v;
...
IsNull.Substitute(v,v.Value,0);
....
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.