अद्यतन : मैंने इस प्रश्न को एक लेख के आधार के रूप में उपयोग किया है जो यहां पाया जा सकता है ; इसे इस मुद्दे की अतिरिक्त चर्चा के लिए देखें। अच्छे प्रश्न के लिए धन्यवाद!
हालांकि शबसे का उत्तर निश्चित रूप से सही है और पूछे गए प्रश्न का उत्तर देता है, आपके प्रश्न पर एक महत्वपूर्ण संस्करण है जो आपने नहीं किया है:
यदि निर्माणकर्ता द्वारा अप्रबंधित संसाधन आवंटित किए जाने के बादfont4 = new Font() फेंकता है तो क्या होता है, लेकिन संदर्भ से पहले ही ctor रिटर्न और भरता है ?font4
मैं इसे थोड़ा और स्पष्ट कर दूं। मान लीजिए हमारे पास:
public sealed class Foo : IDisposable
{
private int handle = 0;
private bool disposed = false;
public Foo()
{
Blah1();
int x = AllocateResource();
Blah2();
this.handle = x;
Blah3();
}
~Foo()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!this.disposed)
{
if (this.handle != 0)
DeallocateResource(this.handle);
this.handle = 0;
this.disposed = true;
}
}
}
अब हमारे पास है
using(Foo foo = new Foo())
Whatever(foo);
यह भी ऐसा ही है
{
Foo foo = new Foo();
try
{
Whatever(foo);
}
finally
{
IDisposable d = foo as IDisposable;
if (d != null)
d.Dispose();
}
}
ठीक है। मान Whateverलो फेंकता है। फिर finallyब्लॉक चलता है और संसाधन समाप्त हो जाता है। कोई दिक्कत नहीं है।
मान Blah1()लो फेंकता है। फिर थ्रो संसाधन के आवंटित होने से पहले होता है। ऑब्जेक्ट को आवंटित किया गया है लेकिन ctor कभी नहीं लौटता है, इसलिए fooकभी भी भरा नहीं जाता है। हमने कभी भी प्रवेश नहीं किया है tryइसलिए हम कभी भी प्रवेश नहीं करते finallyहैं। वस्तु संदर्भ अनाथ कर दिया गया है। आखिरकार जीसी की खोज की जाएगी और इसे अंतिम कतार में रखा जाएगा। handleअभी भी शून्य है, इसलिए फाइनल कुछ नहीं करता है। ध्यान दें कि अंतिम रूप से उस वस्तु के सामने मजबूत होना आवश्यक है जिसे अंतिम रूप दिया जा रहा है जिसका निर्माता कभी पूरा नहीं हुआ । आपको अंतिम रूप से लिखने की आवश्यकता है जो यह मजबूत हो। यह अभी तक एक और कारण है कि आपको विशेषज्ञों को अंतिम रूप देना चाहिए और इसे स्वयं करने का प्रयास नहीं करना चाहिए।
मान लीजिए Blah3() लो फेंकता है। संसाधन आवंटित होने के बाद थ्रो होता है। लेकिन फिर, fooकभी भी भरा नहीं जाता है, हम कभी भी प्रवेश नहीं करते हैंfinally , और ऑब्जेक्ट को अंतिम थ्रेड द्वारा साफ किया जाता है। इस बार हैंडल नॉन-जीरो है, और फाइनली इसे साफ करता है। फ़ाइनलीज़र फिर से एक ऐसी वस्तु पर चल रहा है जिसका निर्माता कभी सफल नहीं हुआ, लेकिन फ़ाइनलीज़र वैसे भी चलता है। जाहिर है कि इस बार क्योंकि यह काम करना था।
अब मान Blah2()लो फेंकता है। थ्रो संसाधन के आबंटित होने के बाद लेकिन पहले होता है handle भरने ! फिर से, फाइनल चलेगा लेकिन अब handleभी शून्य है और हम हैंडल को लीक कर रहे हैं!
इस रिसाव को होने से रोकने के लिए आपको बेहद चतुर कोड लिखना होगा । अब, अपने Fontसंसाधन के मामले में , कौन परवाह करता है? हम एक फ़ॉन्ट संभाल, बड़ी बात लीक करते हैं। लेकिन अगर आपको पूरी तरह से सकारात्मक रूप से आवश्यकता है कि हर अप्रबंधित संसाधन को साफ किया जाए तो कोई फर्क नहीं पड़ता कि अपवादों का समय क्या है, तो आपके हाथों में एक बहुत मुश्किल समस्या है।
सीएलआर को ताले के साथ इस समस्या को हल करना होगा। C # 4 के बाद से, lockकथन का उपयोग करने वाले ताले इस तरह से लागू किए गए हैं:
bool lockEntered = false;
object lockObject = whatever;
try
{
Monitor.Enter(lockObject, ref lockEntered);
lock body here
}
finally
{
if (lockEntered) Monitor.Exit(lockObject);
}
Enterबहुत सावधानी से लिखा गया है ताकि कोई फर्क नहीं पड़ता कि क्या अपवाद हैं , lockEnteredयह निर्धारित करने के लिए सही है कि क्या और केवल अगर ताला वास्तव में लिया गया था। यदि आपके पास समान आवश्यकताएं हैं, तो आपको वास्तव में जो लिखना है, वह है:
public Foo()
{
Blah1();
AllocateResource(ref handle);
Blah2();
Blah3();
}
और AllocateResourceचतुराई से लिखो Monitor.Enterताकि कोई फर्क नहीं पड़ता कि अंदर क्या होता है AllocateResource, अगर और केवल अगरhandle में भरा है उसे निपटाया जाए।
ऐसा करने के लिए तकनीकों का वर्णन करना इस उत्तर के दायरे से परे है। यदि आपके पास यह आवश्यकता है, तो एक विशेषज्ञ से परामर्श करें।