2010-09-02 5 views
6

एक और सवाल पर एसओ सी # में इस कोड की कोशिश करने के लिए मुझे प्रेरित किया:अपने स्थिर कन्स्ट्रक्टर में एक वर्ग का उदाहरण बनाना - इसकी अनुमति क्यों है?

class Program 
{ 
    static Program() 
    { 
     new Program().Run(); 
    } 

    static void Main(string[] args) { } 

    void Run() 
    { 
     System.Console.WriteLine("Running"); 
    } 
} 

यह प्रिंट "चल रहा" जब रन।

मुझे वास्तव में संकलक इस बारे में शिकायत करने की उम्मीद करता था। आखिरकार, यदि कक्षा अभी तक स्थिर निर्माता द्वारा शुरू नहीं की गई है; हम कैसे सुनिश्चित कर सकते हैं कि यह कॉल विधियों के लिए मान्य है?

तो संकलक हमें ऐसा करने से क्यों प्रतिबंधित नहीं करता है? क्या इसके लिए कोई महत्वपूर्ण उपयोग परिदृश्य है?

संपादित

मैं सिंगलटन पैटर्न के बारे में पता कर रहा हूँ; सवाल यह है कि मैं अपने स्थिर कन्स्ट्रक्टर खत्म होने से पहले उदाहरण पर एक विधि क्यों कह सकता हूं। अभी तक जेरेडपार के जवाब के बारे में कुछ अच्छा तर्क है।

+1

एक सिंगलटन पैटर्न के साथ, यह वास्तव में * सर्वोत्तम अभ्यास * स्थिर रचनाकार (या स्थिर क्षेत्र प्रारंभकर्ता में, जो एक ही चीज़ के बराबर है) में संलग्न कक्षा को तुरंत चालू करने के लिए है। –

उत्तर

6

जैसे कुछ पैटर्न के लिए भी उपयोगी/संभवतः आवश्यक है क्योंकि यह लॉट खराब होने की अनुमति नहीं है। इस तरह का कोड बुरी तरह खराब हो जाएगा:

class A { 
    public static readonly A a; 
    public static readonly B b; 
    static A() { 
     b = new B(); 
     a = B.a; 
    } 
} 

class B { 
    public static readonly A a; 
    public static readonly B b; 
    static B() { 
     a = new A(); 
     b = A.b; 
    } 
} 

आप निश्चित रूप से अपने पैर पर एक लोड बंदूक को इंगित कर रहे हैं।

एक प्रकार विशेषता beforefieldinit के साथ चिह्नित किया जा सकता है (§10.1.6) से संकेत मिलता है:

यह व्यवहार CLI युक्ति (ECMA 335) विभाजन द्वितीय, अध्याय 10.5.3.2 "आराम की गारंटी देता है" में दर्ज है कि §10.5.3.1 में निर्दिष्ट गारंटी आवश्यक रूप से आवश्यक नहीं है। विशेष रूप से, उपरोक्त अंतिम आवश्यकता प्रदान नहीं की जानी चाहिए: स्थिर विधि को संदर्भित या संदर्भित करने से पहले टाइप प्रारंभकर्ता को निष्पादित करने की आवश्यकता नहीं है।

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

सी # संकलक वास्तव में beforefieldinit एक वर्ग में यह विशेषता का उत्सर्जन करता है:

.class private auto ansi beforefieldinit ConsoleApplication2.Program 
     extends [mscorlib]System.Object 
{ 
    // etc... 
} 
0

स्टेटिक कन्स्ट्रक्टर केवल स्थिर वर्ग के सदस्यों को प्रारंभ कर सकता है, यह कक्षा के उदाहरणों और नियमित गैर स्थैतिक वर्ग के सदस्यों से संबंधित नहीं है।

6

थोड़ा अलग सवाल।

संकलक आपको ऐसा करने से कैसे रोक देगा?

निश्चित रूप से आपके नमूने में पता लगाना बहुत आसान है, लेकिन इस नमूने के बारे में क्या?

class Program { 
    static void Fun() { 
    new Program(); 
    } 
    static Program() { 
    Fun(); 
    } 
} 

जिस तरीके से आप इसे संकलक करने के लिए कंपाइलर को चालित कर सकते हैं, वस्तुतः अंतहीन हैं। यहां तक ​​कि अगर कंपाइलर को सभी जवाब मिलते हैं तो भी आप इसे प्रतिबिंब के साथ हरा सकते हैं।

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

+1

+1, एक अच्छा उदाहरण है। – driis

0

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

इस विस्तार का प्रयास करें:

class Program 
{ 
    static Program() 
    { 
     new Program().Run(); 
    } 

    public Program() 
    { 
     Console.WriteLine("Instantiating a Program"); 
    } 

    public override void Finalize() 
    { 
     Console.WriteLine("Finalizing a Program"); 
    } 

    static void Main(string[] args) { Console.WriteLine("main() called"); } 

    void Run() 
    { 
     System.Console.WriteLine("Running"); 
    } 
} 

देखें क्या उत्पादन होता है। ,

Instantiating a Program 
Running 
Finalizing a Program 
main() called 

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

संबंधित मुद्दे