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


125

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

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

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


11
आप जो पूछते हैं उससे संबंधित नहीं है, लेकिन इस बात से अवगत रहें कि new TimeSpan(2000)2000 मिलीसेकंड का मतलब यह नहीं है, इसका मतलब 2000 "टिक" है जो 0.2 मिलीसेकंड है, या 10,000 सेकंड का दो सेकंड है।
जेपी स्टिग नील्सन

जवाबों:


173

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

void Foo(TimeSpan? span = null) {

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

   ...

}

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

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


15
अब डिफ़ॉल्ट मान को दस्तावेज़ में रखें <param>, क्योंकि यह हस्ताक्षर में दिखाई नहीं देता है।
कर्नल पैनिक

3
मैं ऐसा नहीं कर सकता, मैं कुछ और के लिए विशेष मूल्य शून्य का उपयोग कर रहा हूं।
कर्नल पैनिक

4
@MattHickford - फिर आपको एक अधिभार विधि प्रदान करनी होगी या पैरामीटर के रूप में मिलीसेकंड लेना होगा।
जोश

19
भी span = span ?? TimeSpan.FromSeconds(2.0);अशक्त प्रकार के साथ उपयोग कर सकते हैं , विधि शरीर में। या var realSpan = span ?? TimeSpan.FromSeconds(2.0);एक स्थानीय चर प्राप्त करने के लिए जो अशक्त नहीं है।
जेपी स्टिग नीलसन

5
मुझे इस बारे में पसंद नहीं है कि यह फ़ंक्शन के उपयोगकर्ता से तात्पर्य है कि यह फ़ंक्शन शून्य अवधि के साथ "काम करता है"। लेकिन यह सच नहीं है! जहां तक ​​फ़ंक्शन के वास्तविक तर्क का सवाल है, अशक्त स्पैन के लिए एक मान्य मूल्य नहीं है। काश एक बेहतर तरीका होता जो कोड गंध की तरह प्रतीत नहीं होता ...
जोकूल

31

मेरी VB6 विरासत मुझे "शून्य मान" और "लापता मूल्य" के समकक्ष होने के विचार के साथ असहज करती है। ज्यादातर मामलों में, यह शायद ठीक है, लेकिन आपके पास एक अनपेक्षित दुष्प्रभाव हो सकता है, या आप एक असाधारण स्थिति को निगल सकते हैं (उदाहरण के लिए, यदि स्रोत spanएक संपत्ति या चर है जो शून्य नहीं होना चाहिए, लेकिन है)।

इसलिए मैं इस विधि को अधिभारित करूंगा:

void Foo()
{
    Foo(TimeSpan.FromSeconds(2.0));
}
void Foo(TimeSpan span)
{
    //...
}

1
उस महान तकनीक के लिए +1। डिफ़ॉल्ट मापदंडों का उपयोग केवल कास्ट प्रकारों के साथ किया जाना चाहिए, वास्तव में। और, यह अविश्वसनीय है।
लेज्लो

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

23

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

void Foo(TimeSpan span = default(TimeSpan))


4
ढेर अतिप्रवाह में आपका स्वागत है। आपका उत्तर प्रतीत होता है कि आप एक डिफ़ॉल्ट पैरामीटर मान प्रदान कर सकते हैं, जब तक कि यह एक बहुत विशिष्ट मूल्य है जो कंपाइलर अनुमति देता है। क्या मैं सही समझ गया हूं? (आप अपने उत्तर को स्पष्ट करने के लिए संपादित कर सकते हैं ।) यह एक बेहतर उत्तर होगा यदि यह दिखाया जाए कि संकलक ने उस लाभ का लाभ कैसे उठाया, जो मूल रूप से मांगे गए प्रश्न को प्राप्त करने की अनुमति देता है, जो कि अन्य TimeSpan मानों के विपरीत है, जैसे कि इसके द्वारा दिया गया new TimeSpan(2000)
रोब कैनेडी

2
एक विकल्प जो कुछ विशिष्ट डिफ़ॉल्ट मान का उपयोग करता है, वह एक निजी स्टेटिकली टाइमस्पैन डिफ़ॉल्टटाइम्सपेन = Timespan.FromSeconds (2) का उपयोग करने के लिए होगा, जो कि डिफ़ॉल्ट कंस्ट्रक्टर और कंस्ट्रक्टर के साथ संयुक्त रूप से एक टेंपन ले रहा है। सार्वजनिक फू (): यह (डिफ़ॉल्ट रूप से) और सार्वजनिक फू (
टाइमपेन

15

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

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

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

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

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

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


2
थोड़ा नाइट-पिक: किसी स्थिर सूची को कॉल करना किसी भी सूची में फिट नहीं होता है। TimeSpanइस सूची में अंतिम एक फिट हो सकता default(TimeSpan)है।
कोडइन्चोस

12
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लिए एक वैध मान होने की संभावना दुर्लभ है।


4

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

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

3

मेरा सुझाव:

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

    //...
}

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


2

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

इस तरह की विधि के लिए C # डिफ़ॉल्ट पैरामीटर का उपयोग करने का एक विकल्प यह होगा कि जिस पैटर्न का उपयोग किया गया है, उसका अनुकरण किया जाए 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ऑब्जेक्ट के एक उदाहरण का पुन: उपयोग करना बहुत आसान है ।

लाभ

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

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

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