C# Language
कंस्ट्रक्टर और फाइनल
खोज…
परिचय
कंस्ट्रक्टर्स एक वर्ग में एक विधि है जो उस वर्ग का एक उदाहरण बनने पर आह्वान किया जाता है। उनकी मुख्य जिम्मेदारी एक उपयोगी और सुसंगत स्थिति में नई वस्तु को छोड़ना है।
विध्वंसक / फ़ाइनलाइज़र एक वर्ग में ऐसी विधियाँ हैं, जिन्हें तब नष्ट किया जाता है जब उसका एक उदाहरण नष्ट हो जाता है। C # में वे शायद ही कभी लिखे / प्रयोग किए जाते हैं।
टिप्पणियों
C # में वास्तव में विध्वंसक नहीं होते हैं, बल्कि फ़ाइनलाइज़र होते हैं जो C ++ स्टाइल डिस्ट्रक्टर सिंटैक्स का उपयोग करते हैं। एक विध्वंसक निर्दिष्ट करना Object.Finalize()
विधि को ओवरराइड करता है जिसे सीधे नहीं कहा जा सकता है।
समान सिंटैक्स वाली अन्य भाषाओं के विपरीत, इन विधियों को तब नहीं बुलाया जाता है जब ऑब्जेक्ट दायरे से बाहर हो जाते हैं, लेकिन इसे तब बुलाया जाता है जब कचरा कलेक्टर चलता है, जो कुछ शर्तों के तहत होता है। जैसे, उन्हें किसी विशेष क्रम में चलने की गारंटी नहीं है ।
फाइनल केवल अप्रबंधित संसाधनों की सफाई के लिए ज़िम्मेदार होना चाहिए (पी / इनवोक (सिस्टम कॉल) या असुरक्षित ब्लॉकों के भीतर उपयोग किए जाने वाले कच्चे पॉइंटर्स के माध्यम से प्राप्त मार्शल क्लास के माध्यम से प्राप्त किए गए पॉइंटर्स)। प्रबंधित संसाधनों को साफ करने के लिए, कृपया आईडीसॉर्पोरेटरी, डिस्पोज़ पैटर्न और using
कथन की समीक्षा करें।
(आगे पढ़े: मुझे विध्वंसक कब बनाना चाहिए? )
डिफ़ॉल्ट निर्माता
जब एक प्रकार को एक निर्माता के बिना परिभाषित किया जाता है:
public class Animal
{
}
तब कंपाइलर एक डिफ़ॉल्ट कंस्ट्रक्टर को निम्न के बराबर बनाता है:
public class Animal
{
public Animal() {}
}
प्रकार के लिए किसी भी निर्माता की परिभाषा डिफ़ॉल्ट निर्माता पीढ़ी को दबाएगी। यदि प्रकार निम्नानुसार परिभाषित किया गया था:
public class Animal
{
public Animal(string name) {}
}
तब एक Animal
केवल घोषित कंस्ट्रक्टर को बुलाकर बनाया जा सकता था।
// This is valid
var myAnimal = new Animal("Fluffy");
// This fails to compile
var unnamedAnimal = new Animal();
दूसरे उदाहरण के लिए, कंपाइलर एक त्रुटि संदेश प्रदर्शित करेगा:
'एनिमल' में एक कंस्ट्रक्टर नहीं होता है जो 0 तर्क देता है
यदि आप चाहते हैं कि एक वर्ग दोनों के पास एक पैरामीटर रहित निर्माता और एक निर्माता है जो एक पैरामीटर लेता है, तो आप इसे स्पष्ट रूप से अन्य निर्माणकर्ताओं को लागू करके कर सकते हैं।
public class Animal
{
public Animal() {} //Equivalent to a default constructor.
public Animal(string name) {}
}
कंपाइलर एक डिफ़ॉल्ट कंस्ट्रक्टर उत्पन्न करने में सक्षम नहीं होगा यदि वर्ग एक अन्य वर्ग का विस्तार करता है जिसमें एक पैरामीटर रहित निर्माता नहीं है। उदाहरण के लिए, यदि हमारे पास एक वर्ग Creature
:
public class Creature
{
public Creature(Genus genus) {}
}
तब Animal
को class Animal : Creature {}
रूप में परिभाषित किया गया class Animal : Creature {}
संकलन नहीं करेगा।
एक कंस्ट्रक्टर को दूसरे कंस्ट्रक्टर से कॉल करना
public class Animal
{
public string Name { get; set; }
public Animal() : this("Dog")
{
}
public Animal(string name)
{
Name = name;
}
}
var dog = new Animal(); // dog.Name will be set to "Dog" by default.
var cat = new Animal("Cat"); // cat.Name is "Cat", the empty constructor is not called.
स्थैतिक निर्माता
एक स्थिर रचनाकार को पहली बार किसी प्रकार के सदस्य को आरंभीकृत करने वाला कहा जाता है, एक स्थिर वर्ग के सदस्य को एक स्थिर विधि कहा जाता है। स्थिर कंस्ट्रक्टर थ्रेड सुरक्षित है। एक स्थिर निर्माणकर्ता आमतौर पर इसका उपयोग किया जाता है:
- स्टेटिक स्टेट को इनिशियलाइज़ करें, वह राज्य है जो एक ही क्लास के विभिन्न उदाहरणों में साझा किया जाता है।
- एक सिंगलटन बनाएं
उदाहरण:
class Animal
{
// * A static constructor is executed only once,
// when a class is first accessed.
// * A static constructor cannot have any access modifiers
// * A static constructor cannot have any parameters
static Animal()
{
Console.WriteLine("Animal initialized");
}
// Instance constructor, this is executed every time the class is created
public Animal()
{
Console.WriteLine("Animal created");
}
public static void Yawn()
{
Console.WriteLine("Yawn!");
}
}
var turtle = new Animal();
var giraffe = new Animal();
आउटपुट:
पशु ने इनिशियलाइज़ किया
जानवर बनाया
जानवर बनाया
यदि पहली कॉल एक स्थिर विधि के लिए है, तो स्थिर कंस्ट्रक्टर को बिना उदाहरण के कंस्ट्रक्टर के लिए लागू किया जाता है। यह ठीक है, क्योंकि स्टेटिक विधि इंस्टेंस स्टेट वैसे भी एक्सेस नहीं कर सकती है।
Animal.Yawn();
यह आउटपुट होगा:
पशु ने इनिशियलाइज़ किया
जम्हाई!
स्थिर कंस्ट्रक्टर और जेनेरिक स्टेटिक कंस्ट्रक्टर्स में अपवाद भी देखें।
सिंगलटन उदाहरण:
public class SessionManager
{
public static SessionManager Instance;
static SessionManager()
{
Instance = new SessionManager();
}
}
बेस क्लास कंस्ट्रक्टर को कॉल करना
बेस क्लास के एक कंस्ट्रक्टर को एक व्युत्पन्न वर्ग के कंस्ट्रक्टर को निष्पादित करने से पहले बुलाया जाता है। उदाहरण के लिए, यदि Mammal
Animal
विस्तार करता है, तो एक Mammal
का उदाहरण बनाते समय Animal
के निर्माण में निहित कोड को पहले कहा जाता है।
यदि एक व्युत्पन्न वर्ग स्पष्ट रूप से निर्दिष्ट नहीं करता है कि बेस क्लास के किस कंस्ट्रक्टर को बुलाया जाना चाहिए, तो कंपाइलर पैरामीटर रहित कंस्ट्रक्टर को मानता है।
public class Animal
{
public Animal() { Console.WriteLine("An unknown animal gets born."); }
public Animal(string name) { Console.WriteLine(name + " gets born"); }
}
public class Mammal : Animal
{
public Mammal(string name)
{
Console.WriteLine(name + " is a mammal.");
}
}
इस मामले में, new Mammal("George the Cat")
को बुलाकर एक Mammal
को तत्काल प्रिंट किया जाएगा
एक अनजान जानवर पैदा होता है।
जॉर्ज द कैट एक स्तनधारी प्राणी है।
बेस क्लास के किसी अलग कंस्ट्रक्टर को कॉल करना, कंस्ट्रक्टर के सिग्नेचर और उसके बॉडी के बीच : base(args)
रखकर किया जाता है:
public class Mammal : Animal
{
public Mammal(string name) : base(name)
{
Console.WriteLine(name + " is a mammal.");
}
}
new Mammal("George the Cat")
अब प्रिंट करेंगे:
जॉर्ज द कैट जन्म लेता है।
जॉर्ज द कैट एक स्तनधारी प्राणी है।
व्युत्पन्न वर्गों पर फाइनल
जब ऑब्जेक्ट ग्राफ को अंतिम रूप दिया जाता है, तो ऑर्डर निर्माण का उल्टा होता है। जैसे कि निम्न कोड प्रदर्शित करता है, बेस-प्रकार से पहले सुपर-टाइप को अंतिम रूप दिया जाता है:
class TheBaseClass
{
~TheBaseClass()
{
Console.WriteLine("Base class finalized!");
}
}
class TheDerivedClass : TheBaseClass
{
~TheDerivedClass()
{
Console.WriteLine("Derived class finalized!");
}
}
//Don't assign to a variable
//to make the object unreachable
new TheDerivedClass();
//Just to make the example work;
//this is otherwise NOT recommended!
GC.Collect();
//Derived class finalized!
//Base class finalized!
सिंगलटन कंस्ट्रक्टर पैटर्न
public class SingletonClass
{
public static SingletonClass Instance { get; } = new SingletonClass();
private SingletonClass()
{
// Put custom constructor code here
}
}
क्योंकि कंस्ट्रक्टर निजी है, इसलिए SingletonClass
का कोई भी नया उदाहरण उपभोग कोड के द्वारा नहीं बनाया जा सकता है। SingletonClass
के एकल उदाहरण तक पहुँचने का एकमात्र तरीका स्थिर संपत्ति SingletonClass.Instance
का उपयोग करके है।
Instance
प्रॉपर्टी को एक स्थिर कंस्ट्रक्टर द्वारा सौंपा गया है जो C # कंपाइलर जेनरेट करता है। .NET रनटाइम गारंटी देता है कि स्टेटिक कंस्ट्रक्टर को एक बार में चलाया जाता है और Instance
को पहली बार पढ़ने से पहले चलाया जाता है। इसलिए, सभी सिंक्रनाइज़ेशन और इनिशियलाइज़ेशन चिंताओं को रनटाइम द्वारा किया जाता है।
ध्यान दें, कि यदि स्थिर कंस्ट्रक्टर विफल हो जाता है, तो AppDomain के जीवन के लिए Singleton
वर्ग स्थायी रूप से अनुपयोगी हो जाता है।
इसके अलावा, इंस्टेंट कंस्ट्रक्टर को Instance
की पहली पहुंच के समय चलने की गारंटी नहीं है। बल्कि, यह उससे पहले किसी बिंदु पर चलेगा। यह वह समय बनाता है जिस पर प्रारंभिक निर्धारण गैर-नियतात्मक होता है। व्यावहारिक मामलों में JIT अक्सर Instance
संदर्भित करने वाली विधि के संकलन (निष्पादन नहीं) के दौरान स्थिर कंस्ट्रक्टर को कॉल करता है। यह एक प्रदर्शन अनुकूलन है।
सिंगलटन पैटर्न को लागू करने के अन्य तरीकों के लिए सिंगलटन कार्यान्वयन पृष्ठ देखें।
एक स्थिर रचनाकार को बुलाने के लिए मजबूर करना
जबकि स्थिर कंस्ट्रक्टरों को हमेशा एक प्रकार के पहले उपयोग से पहले कहा जाता है, यह कभी-कभी उन्हें बुलाए जाने के लिए सक्षम करने के लिए उपयोगी होता है और RuntimeHelpers
वर्ग इसके लिए एक सहायक प्रदान करता है:
using System.Runtime.CompilerServices;
// ...
RuntimeHelpers.RunClassConstructor(typeof(Foo).TypeHandle);
टिप्पणी : सभी स्टैटिक इनिशियलाइज़ेशन (उदाहरण के लिए फ़ील्ड इनिशियलाइज़र) चलेंगे, न कि केवल कंस्ट्रक्टर ही।
संभावित उपयोग : एक यूआई एप्लिकेशन में स्प्लैश स्क्रीन के दौरान आरंभीकरण को मजबूर करना या यह सुनिश्चित करना कि एक यूनिट परीक्षण में एक स्थिर निर्माण विफल नहीं होता है।
कंस्ट्रक्टर में वर्चुअल तरीके कॉल करना
C ++ में C # के विपरीत आप क्लास कंस्ट्रक्टर से एक वर्चुअल विधि कह सकते हैं (ठीक है, आप C ++ में भी कर सकते हैं लेकिन पहली बार में व्यवहार आश्चर्यजनक है)। उदाहरण के लिए:
abstract class Base
{
protected Base()
{
_obj = CreateAnother();
}
protected virtual AnotherBase CreateAnother()
{
return new AnotherBase();
}
private readonly AnotherBase _obj;
}
sealed class Derived : Base
{
public Derived() { }
protected override AnotherBase CreateAnother()
{
return new AnotherDerived();
}
}
var test = new Derived();
// test._obj is AnotherDerived
यदि आप C ++ पृष्ठभूमि से आते हैं, तो यह आश्चर्य की बात है, बेस क्लास कंस्ट्रक्टर पहले से ही व्युत्पन्न वर्ग वर्चुअल विधि तालिका देखता है!
सावधान रहें : व्युत्पन्न वर्ग को अभी तक पूरी तरह से शुरू नहीं किया जा सकता है (इसका निर्माण बेस क्लास कंस्ट्रक्टर के बाद निष्पादित किया जाएगा) और यह तकनीक खतरनाक है (इसके लिए स्टाइलकॉप चेतावनी भी है)। आमतौर पर इसे बुरी प्रथा माना जाता है।
जेनेरिक स्टेटिक कंस्ट्रक्टर्स
यदि वह प्रकार जिस पर स्थिर कंस्ट्रक्टर घोषित किया गया है जेनेरिक है, तो जेनेरिक तर्कों के प्रत्येक अनूठे संयोजन के लिए स्थिर कंस्ट्रक्टर को एक बार बुलाया जाएगा।
class Animal<T>
{
static Animal()
{
Console.WriteLine(typeof(T).FullName);
}
public static void Yawn() { }
}
Animal<Object>.Yawn();
Animal<String>.Yawn();
यह आउटपुट होगा:
System.Object
System.String
यह भी देखें कि सामान्य प्रकार के कामों के लिए स्थिर निर्माणकर्ता कैसे काम करते हैं?
स्थिर कंस्ट्रक्टर में अपवाद
यदि कोई स्थैतिक निर्माता अपवाद को फेंकता है, तो वह कभी पीछे नहीं हटता है। प्रकार AppDomain के जीवनकाल के लिए अनुपयोगी है। प्रकार का कोई भी उपयोग मूल अपवाद के चारों ओर लिपटे TypeInitializationException
को बढ़ाएगा।
public class Animal
{
static Animal()
{
Console.WriteLine("Static ctor");
throw new Exception();
}
public static void Yawn() {}
}
try
{
Animal.Yawn();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
try
{
Animal.Yawn();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
यह आउटपुट होगा:
स्थैतिक ctor
System.TypeInitializationException: 'एनिमल' के लिए टाइप इनिशियलाइज़र ने एक अपवाद को फेंक दिया। ---> System.Exception: 'System.Exception' टाइप का अपवाद फेंका गया था।
[...]
System.TypeInitializationException: 'एनिमल' के लिए टाइप इनिशियलाइज़र ने एक अपवाद को फेंक दिया। ---> System.Exception: 'System.Exception' टाइप का अपवाद फेंका गया था।
जहां आप देख सकते हैं कि वास्तविक निर्माणकर्ता को केवल एक बार निष्पादित किया जाता है, और अपवाद का फिर से उपयोग किया जाता है।
कंस्ट्रक्टर और प्रॉपर्टी इनिशियलाइज़ेशन
क्या प्रॉपर्टी वैल्यू के असाइनमेंट को क्लास के कंस्ट्रक्टर से पहले या बाद में निष्पादित किया जाएगा?
public class TestClass
{
public int TestProperty { get; set; } = 2;
public TestClass()
{
if (TestProperty == 1)
{
Console.WriteLine("Shall this be executed?");
}
if (TestProperty == 2)
{
Console.WriteLine("Or shall this be executed");
}
}
}
var testInstance = new TestClass() { TestProperty = 1 };
उपरोक्त उदाहरण में, TestProperty
मान कक्षा के कंस्ट्रक्टर में 1
या क्लास कंस्ट्रक्टर के बाद होगा?
इस तरह के निर्माण में संपत्ति मूल्यों को असाइन करना:
var testInstance = new TestClass() {TestProperty = 1};
कंस्ट्रक्टर चलने के बाद निष्पादित किया जाएगा। हालाँकि, वर्ग मान में गुणन मान को C # 6.0 में इस तरह से देखें:
public class TestClass
{
public int TestProperty { get; set; } = 2;
public TestClass()
{
}
}
कंस्ट्रक्टर चलाने से पहले किया जाएगा।
एक ही उदाहरण में ऊपर दो अवधारणाओं का मेल:
public class TestClass
{
public int TestProperty { get; set; } = 2;
public TestClass()
{
if (TestProperty == 1)
{
Console.WriteLine("Shall this be executed?");
}
if (TestProperty == 2)
{
Console.WriteLine("Or shall this be executed");
}
}
}
static void Main(string[] args)
{
var testInstance = new TestClass() { TestProperty = 1 };
Console.WriteLine(testInstance.TestProperty); //resulting in 1
}
अंतिम परिणाम:
"Or shall this be executed"
"1"
स्पष्टीकरण:
TestProperty
मान को पहले 2
रूप में असाइन किया जाएगा, फिर TestClass
कंस्ट्रक्टर चलाया जाएगा, जिसके परिणामस्वरूप मुद्रण होगा
"Or shall this be executed"
और फिर TestProperty
को new TestClass() { TestProperty = 1 }
कारण 1
के रूप में सौंपा जाएगा, जो Console.WriteLine(testInstance.TestProperty)
द्वारा मुद्रित TestProperty
लिए अंतिम मान Console.WriteLine(testInstance.TestProperty)
है
"1"