निर्दिष्ट किए जाने के लिए आवश्यक नहीं है कि संरचना के प्रकार के पैरामीटर


9

मैंने अपने कोड में कुछ विचित्र व्यवहार को देखा है जब गलती से कोड समीक्षा के दौरान एक पंक्ति में एक टिप्पणी कर रहा था। इसे पुन: पेश करना बहुत कठिन था, लेकिन मैं यहां इसी तरह के उदाहरण को चित्रित करूंगा।

मुझे यह परीक्षण वर्ग मिला है:

public class Test
{
    public void GetOut(out EmailAddress email)
    {
        try
        {
            Foo(email);
        }
        catch
        {
        }
    }

    public void Foo(EmailAddress email)
    {
    }
}

ईमेल में कोई असाइनमेंट नहीं है GetOutजिसमें आम तौर पर एक त्रुटि होगी:

वर्तमान पद्धति को नियंत्रित करने से पहले आउट पैरामीटर 'ईमेल' को सौंपा जाना चाहिए

हालाँकि, अगर EmailAddress एक अलग विधानसभा में एक संरचना में है, तो कोई त्रुटि नहीं हुई है और सब कुछ ठीक संकलन करता है।

public struct EmailAddress
{
    #region Constructors

    public EmailAddress(string email)
        : this(email, string.Empty)
    {
    }

    public EmailAddress(string email, string name)
    {
        this.Email = email;
        this.Name = name;
    }

    #endregion

    #region Properties

    public string Email { get; private set; }
    public string Name { get; private set; }

    #endregion
}

संकलक लागू क्यों नहीं करता है कि ईमेल को असाइन किया जाना चाहिए? यदि यह संरचना अलग असेंबली में बनाई गई है, तो यह कोड क्यों संकलित करता है, लेकिन यदि मौजूदा असेंबली में संरचना को परिभाषित किया गया है तो यह संकलन नहीं करता है?


2
यदि आप एक वर्ग का उपयोग कर रहे हैं, तो आपको ऑब्जेक्ट का एक उदाहरण 'नया' करना होगा। यह संरचना के लिए आवश्यक नहीं है। docs.microsoft.com/en-us/dotnet/csharp/programming-guide/... : (उस पृष्ठ पर इस पाठ विशेष रूप से के लिए खोज के विपरीत कक्षाएं, structs नई operato का उपयोग किए बिना instantiated जा सकता है)
— Dortimer

1
जैसे ही आपकी डॉग संरचना एक वैरिएबल हो जाती है वह संकलित नहीं होगी :)
— एंड्रे सेन्सन

इस उदाहरण में, के साथ struct Dog{}, सब ठीक है।
— हेनक होल्टरमैन

2
@ johnny5 फिर उदाहरण दिखाएं।
— आंद्रे सेन्सन

1
ठीक है, यह दिलचस्प है। एक कोर 3 कंसोल ऐप और एक .St वर्ग के लिब के साथ पुन: प्रस्तुत किया गया।
— हेनक होल्टरमैन

जवाबों:


12

TLDR: यह लंबे समय से चली आ रही एक ज्ञात बग है। मैंने पहली बार इसके बारे में 2010 में लिखा था:

https://blogs.msdn.microsoft.com/ericlippert/2010/01/18/a-definite-assignment-anomaly/

यह हानिरहित है और आप इसे सुरक्षित रूप से अनदेखा कर सकते हैं, और कुछ हद तक अस्पष्ट बग खोजने के लिए खुद को बधाई दे सकते हैं।

संकलक लागू क्यों नहीं करता है Emailजिसे निश्चित रूप से सौंपा जाना चाहिए?

ओह, यह एक फैशन में है। यह सिर्फ एक गलत विचार है कि किस स्थिति का अर्थ है कि चर निश्चित रूप से सौंपा गया है, जैसा कि हम देखेंगे।

यदि यह संरचना अलग असेंबली में बनाई गई है, तो यह कोड क्यों संकलित करता है, लेकिन यदि मौजूदा असेंबली में संरचना को परिभाषित किया गया है तो यह संकलन नहीं करता है?

यह बग की जड़ है। बग एक अंतर का परिणाम है कि C # कंपाइलर स्ट्रक्चर्स पर निश्चित असाइनमेंट चेक करता है और कंपाइलर मेटाडेटा को पुस्तकालयों से कैसे लोड करता है।

इस पर विचार करो:

struct Foo 
{ 
  public int x; 
  public int y; 
}
// Yes, public fields are bad, but this is just 
// to illustrate the situation.
void M(out Foo f)
{

ठीक है, इस बिंदु पर हम क्या जानते हैं? fप्रकार के एक चर के लिए एक उपनाम है Foo, इसलिए भंडारण पहले से ही आवंटित किया गया है और निश्चित रूप से कम से कम राज्य में है कि यह भंडारण आवंटन से बाहर आ गया। यदि कॉल करने वाले द्वारा चर में कोई मान रखा गया था, तो वह मान है।

हमें क्या चाहिए? हमें आवश्यक है कि fनिश्चित रूप से किसी भी बिंदु पर नियत किया जाए जहां नियंत्रण Mसामान्य रूप से रहता है। तो आप कुछ इस तरह की उम्मीद करेंगे:

void M(out Foo f)
{
  f = new Foo();
}

जो सेट f.xऔर f.yउनके डिफ़ॉल्ट मानों के लिए। लेकिन इससे क्या?

void M(out Foo f)
{
  f = new Foo();
  f.x = 123;
  f.y = 456;
}

वह भी ठीक होना चाहिए। लेकिन, और यहां किकर है, हमें केवल एक पल बाद उन्हें उड़ाने के लिए डिफ़ॉल्ट मानों को असाइन करने की आवश्यकता क्यों है? यह देखने के लिए कि हर क्षेत्र सौंपा गया है, सी # निश्चित असाइनमेंट चेकर चेक ! यह कानूनी है:

void M(out Foo f)
{
  f.x = 123;
  f.y = 456;
}

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

सही। तो क्या बग है?

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

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

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

और निश्चित रूप से बग उन स्ट्रक्चर्स के निजी क्षेत्रों के लिए रिप्रो नहीं करता है जो आपके प्रोजेक्ट में स्रोत कोड में हैं, क्योंकि जाहिर है कंपाइलर के पास पहले से ही निजी क्षेत्रों के बारे में जानकारी है।


@ johnny5: आपको त्रुटियां नहीं करनी चाहिए। Dotnetfiddle.net/ZEKiUk देखें । क्या आप एक साधारण रेपो पोस्ट कर सकते हैं?
— एरिक लिपर्ट

1
फिडेल के लिए धन्यवाद क्योंकि मैं सदस्यों के बजाय गुण के रूप में x और y को परिभाषित करता था
— जॉनी 5

1
@ johnny5: यदि आपने केवल एक सामान्य C # 1.0 स्टाइल प्रॉपर्टी को परिभाषित किया है, तो निश्चित असाइनमेंट चेकर के नजरिए से, यह एक तरीका है, फील्ड नहीं। यदि आपने C # 3.0+ शैली की स्वचालित संपत्ति परिभाषित की है, तो संकलक जानता है कि एक निजी क्षेत्र इसका समर्थन कर रहा है; उस चीज़ के निश्चित असाइनमेंट के नियमों को पिछले कुछ वर्षों में देखा गया है और मुझे अब सटीक नियमों की याद नहीं है।
— एरिक लिपर्ट

यदि आप System.TimeSpanइसके बजाय उपयोग करते हैं, तो त्रुटियां आती हैं: error CS0269: Use of unassigned out parameter 'email'और error CS0177: The out parameter 'email' must be assigned to before control leaves the current method। TimeSpanअर्थात् केवल एक गैर-स्थिर क्षेत्र है _ticks। यह internalअपने विधानसभा mscorlib के लिए है। क्या यह विधानसभा विशेष है? के साथ भी System.DateTime, और इसका क्षेत्र हैprivate
— जेपी स्टिग नीलसन

@JeppeStigNielsen: मुझे नहीं पता कि इसके साथ क्या हो रहा है! यदि आप इसका पता लगाते हैं, तो कृपया मुझे बताएं।
— एरिक लिपर्ट

1

जबकि यह एक बग की तरह लग रहा है यह कुछ मतलब है।

'मिसिंग एरर' केवल क्लास लाइब्रेरी का उपयोग करते समय दिखाई देता है। और एक क्लास लाइब्रेरी को दूसरी .net भाषा में लिखा जा सकता है, जैसे VB.Net। 'निश्चित असाइनमेंट ट्रैकिंग' फ्रेमवर्क की नहीं, C # की एक विशेषता है।

तो कुल मिलाकर मुझे नहीं लगता कि यह एक बग है लेकिन मैं इसके लिए एक आधिकारिक बयान के बारे में नहीं जानता।


यदि आप किसी असेंबली को C # में आयात कर रहे हैं, भले ही वह संरचना किसी अन्य भाषा में लिखी गई असेंबली में झूठ हो सकती है, तो इसका उपयोग करने वाला कोड अभी भी c # में है, इसलिए इसे निश्चित असाइनमेंट ट्रैकिंग का उपयोग नहीं करना चाहिए।
— जॉनी 5

1
जरुरी नहीं। C # आपको एक इकाईीकृत (स्थानीय) चर का उपयोग करने की अनुमति नहीं देगा, लेकिन एक ही समय में यह गारंटी देता है कि इसे 0 ( default(T)) पर सेट किया जाएगा । तो स्मृति-सुरक्षा या कुछ इसी तरह का उल्लंघन नहीं है।
— हेनक होल्टरमैन

3
मैं इस तरह का आधिकारिक बयान दे सकता हूं। :) यह एक लंबे समय से ज्ञात बग है।
— एरिक लिपर्ट

1
@ EricLippert धन्यवाद, मैं उम्मीद कर रहा था कि आप इसे किसी बिंदु पर देखेंगे
— जॉनी 5
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.