c# सी#4.0: क्या मैं एक डिफ़ॉल्ट पैरामीटर के साथ एक वैकल्पिक पैरामीटर के रूप में टाइमस्पेन का उपयोग कर सकता हूं?




c#-4.0 default-value (7)

इनमें से दोनों एक त्रुटि उत्पन्न करते हैं कि वे एक संकलन-समय स्थिर होना चाहिए:

void Foo(TimeSpan span = TimeSpan.FromSeconds(2.0))
void Foo(TimeSpan span = new TimeSpan(2000))

सबसे पहले, क्या कोई यह समझा सकता है कि इन मानों को संकलित समय पर क्यों निर्धारित नहीं किया जा सकता है? और वैकल्पिक टाइमस्पैन ऑब्जेक्ट के लिए डिफ़ॉल्ट मान निर्दिष्ट करने का कोई तरीका है?


यह ठीक काम करता है:

void Foo(TimeSpan span = default(TimeSpan))


मानों का सेट जिसे डिफ़ॉल्ट मान के रूप में उपयोग किया जा सकता है वही है जो एक विशेषता तर्क के लिए उपयोग किया जा सकता है। इसका कारण यह है कि डिफ़ॉल्ट मान डिफ़ॉल्ट DefaultParameterValueAttribute अंदर मेटाडेटा में एन्कोड किए गए हैं।

क्यों संकलन समय पर इसे निर्धारित नहीं किया जा सकता है। संकलन समय पर अनुमत मानों पर मूल्यों और अभिव्यक्तियों का सेट आधिकारिक सी # भाषा spec में सूचीबद्ध है:

सी # 6.0 - गुण पैरामीटर प्रकार :

एक विशेषता वर्ग के लिए स्थितित्मक और नामित पैरामीटर के प्रकार विशेषता पैरामीटर प्रकार तक सीमित हैं, जो हैं:

  • निम्न प्रकारों में से एक: bool , byte , char , double , float , int , long , sbyte , short , string , uint , ulong , ushort
  • प्रकार object
  • टाइप System.Type टाइप करें। टाइप करें।
  • एक enum प्रकार।
    (बशर्ते इसमें सार्वजनिक पहुंच हो और जिन प्रकारों में इसे नेस्टेड किया गया हो (यदि कोई हो) सार्वजनिक पहुंच भी हो)
  • उपरोक्त प्रकार के एकल-आयामी सरणी।

टाइप TimeSpan इन सूचियों में से किसी एक में फिट नहीं है और इसलिए निरंतर उपयोग नहीं किया जा सकता है।


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

ऐसी विधि के लिए सी # डिफ़ॉल्ट पैरामीटर का उपयोग करने का एक विकल्प XmlReaderSettings द्वारा उदाहरण के पैटर्न का उपयोग करना होगा। इस पैटर्न में, पैरामीटर रहित कन्स्ट्रक्टर और सार्वजनिक रूप से लिखने योग्य गुणों वाले वर्ग को परिभाषित करें। फिर इस विधि के ऑब्जेक्ट के साथ अपनी विधि में डिफ़ॉल्ट के साथ सभी विकल्पों को प्रतिस्थापित करें। इसके लिए null डिफ़ॉल्ट को निर्दिष्ट करके भी इस ऑब्जेक्ट को वैकल्पिक बनाएं। उदाहरण के लिए:

public class FooSettings
{
    public TimeSpan Span { get; set; } = TimeSpan.FromSeconds(2);

    // I imagine that if you had a heavyweight default
    // thing you’d want to avoid instantiating it right away
    // because the caller might override that parameter. So, be
    // lazy! (Or just directly store a factory lambda with Func<IThing>).
    Lazy<IThing> thing = new Lazy<IThing>(() => new FatThing());
    public IThing Thing
    {
        get { return thing.Value; }
        set { thing = new Lazy<IThing>(() => value); }
    }

    // Another cool thing about this pattern is that you can
    // add additional optional parameters in the future without
    // even breaking ABI.
    //bool FutureThing { get; set; } = true;

    // You can even run very complicated code to populate properties
    // if you cannot use a property initialization expression.
    //public FooSettings() { }
}

public class Bar
{
    public void Foo(FooSettings settings = null)
    {
        // Allow the caller to use *all* the defaults easily.
        settings = settings ?? new FooSettings();

        Console.WriteLine(settings.Span);
    }
}

कॉल करने के लिए, एक अभिव्यक्ति में सभी को तत्काल और असाइन करने के लिए उस अजीब वाक्यविन्यास का उपयोग करें:

bar.Foo(); // 00:00:02
bar.Foo(new FooSettings { Span = TimeSpan.FromDays(1), }); // 1.00:00:00
bar.Foo(new FooSettings { Thing = new MyCustomThing(), }); // 00:00:02

downsides

इस समस्या को हल करने के लिए यह वास्तव में हेवीवेट दृष्टिकोण है। यदि आप एक त्वरित और गंदे आंतरिक इंटरफ़ेस लिख रहे हैं और TimeSpan कमजोर बना रहे हैं और आपके वांछित डिफ़ॉल्ट मान की तरह शून्य की तरह ठीक काम करेंगे, तो इसके बजाय ऐसा करें।

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

लाभ

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

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


मेरा सुझाव:

void A( long spanInMs = 2000 )
{
    var ts = TimeSpan.FromMilliseconds(spanInMs);

    //...
}

बीटीडब्लू TimeSpan.FromSeconds(2.0) new TimeSpan(2000) बराबर नहीं है - कन्स्ट्रक्टर टिक लेता है।


TimeSpan DefaultValueAttribute लिए एक विशेष मामला है और किसी भी स्ट्रिंग का उपयोग करके निर्दिष्ट किया गया है जिसे TimeSpan.Parse विधि के माध्यम से पार्स किया जा सकता है।

[DefaultValue("0:10:0")]
public TimeSpan Duration { get; set; }

आप अपने हस्ताक्षर को बदलकर इस बारे में बहुत आसानी से काम कर सकते हैं।

void Foo(TimeSpan? span = null) {

   if (span == null) { span = TimeSpan.FromSeconds(2); }

   ...

}

मुझे विस्तृत करना चाहिए - कारण आपके उदाहरण में उन अभिव्यक्ति संकलन-समय स्थिरांक नहीं हैं क्योंकि संकलन समय पर, कंपाइलर बस टाइमस्पेन को निष्पादित नहीं कर सकता है। फ्रॉमसेकंड (2.0) और परिणाम के बाइट्स को आपके संकलित कोड में चिपकाएं।

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


void Foo(TimeSpan span = default(TimeSpan))
{
    if (span == default(TimeSpan)) 
        span = TimeSpan.FromSeconds(2); 
}

प्रदान किया गया default(TimeSpan) फ़ंक्शन के लिए मान्य मान नहीं है।

या

//this works only for value types which TimeSpan is
void Foo(TimeSpan span = new TimeSpan())
{
    if (span == new TimeSpan()) 
        span = TimeSpan.FromSeconds(2); 
}

बशर्ते new TimeSpan() मान्य मान नहीं है।

या

void Foo(TimeSpan? span = null)
{
    if (span == null) 
        span = TimeSpan.FromSeconds(2); 
}

यह बेहतर होना चाहिए कि कार्य के लिए मान्य मान होने के null मूल्य की संभावना दुर्लभ है।







optional-parameters