حافظه در
.NET مدیریت
شده است. مدیریت
شده بدین معنی
که اگر شیءای
در حافظه باشد
و به آن اشارهای
نشود، این شیء
از دید
Framework یک
آشغال است و
جمعآوری
(پاک) میشود. به
این عمل
Garbage
Collection میگویند.
اما بسیاری از
منابع مدیریت
شده نیستند. مثلا
برنامهنویس
باید فایلی را
که باز کرده،
بعد از استفاده،
خود، ببندد. همین
طور اتصال به
database باید
توسط خود
برنامهنویس
قطع شود و ... میتوان
آزادسازی این منابع
را در مخرب
کلاس انجام
داد. در
C# مخربها
درست مانند
C++ تعریف
میشوند.
class SomeClass
{
~SomeClass()
{
}
...
}
|
این تابع پس
از کامپایل
شدن تبدیل به
تابعی
virtual
با نام
Finalize() میشود.
در واقع کد
بالا پس از
کامپایل شدن
فرمی پیدا میکند
که انگار کد
زیر نوشته
شده:
class SomeClass
{
virtual void Finalize()
{
try
{
}
finally
{
base.Finalize();
}
}
...
}
|
تابع
Finalize() یا مخرب را
فقط
Garbage Collector (GC) میتواند
صدا بزند نه
برنامهنویس. همچنین
برنامهنویس
نمیتواند
تابع
Finalize() را خودش
تعریف کند. هر
گونه شباهت
توابع برنامهنویس
به تابع
Finalize() بسته
به میزان
شباهت موجب
ایجاد
warning یا
error میشود.
تنها و تنها تابع
مخرب به فرم
C++ مجاز
است.
اگر
GC شیءای را
پاک کند،
مخربش را هم
صدا میزند. اما
معلوم نیست چه
زمانی
Garbage Collector به سراغ
شیء میرود. اگر
به شیء تنها
یک
reference
اشاره کند با
مساوی
null قرار دادن
آن
reference،
شیء برای جمعآوری
شدن علامت میخورد
و به صف آشغالها
اضافه میشود.
حال اگر
Garbage
Collector را
مجبور کنیم
تمام آشغالها
را پاک کند،
شیء ما میان
مابقی آشغالها
پاک میشود. این
کار زمانبر
است چون که به
بهانه پاک
کردن یک شیء
کل آشغالها
پاک خواهند
شد.
SomeClass someObject = new SomeClass();
...
someObject = null;
GC.Collect();
|
راه دیگر
استفاده از
IDisposable است. اگر
کلاسی
IDisposable را
پیاده
(implement)
کند، میتواند
از متد
Dispose() برای
آزاد کردن
منابع
استفاده کند. این
تابع را
برنامهنویس
میتواند
صریحا صدا
بزند. همان
گونه که از
اسم آن پیداست
کلاسی که
IDisposable را پیاده
کند،
disposable یا قابل دورانداختن
میشود.
public interface IDisposable
{
void Dispose();
}
|
در پیادهسازی
تابع
Dispose() باید
GC.SuppressFinalize() هم صدا زده
شود تا دیگر
GC شیء
مذکور را برای
بار دوم پاک
نکند. برای
پیاده کردن
همچین
سناریویی از
یک متغیر
Boolean که اینجا
اسمش را
گذاشتیم
disposed و دو
نسخه از تابع
Dispose() که
یکی از آن دو متغیری
Boolean
به عنوان
پارامتر میگیرد،
به شکل زیر
استفاده میشود.
public class SomeClass
{
public SomeClass()
{
disposed = false;
}
~SomeClass()
{
dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if(!disposed)
{
if(disposing)
{
}
disposed = true;
}
}
...
private bool disposed;
}
|
متغیر
disposed موجب
میشود که قطعه
کد آزادسازی تنها
یک بار اجرا
شود. اگر این
کار توسط
فراخوانی
تابع
Dispose() انجام شود
کلیه منابع چه
مدیریت شده چه
مدیریت نشده
آزاد میشوند،
چرا که دیگر
قرار نیست
GC به
سراغ آنها
برود. اما اگر
مخرب این کار
را انجام دهد
فقط منابع مدیریت
نشده آزاد میشوند،
چرا که منابع
مدیریت شده
قبلا توسط خود
GC
آزاد شدهاند.
یک راه کوتاه
و تمیز برای
استفاده و بلافاصله
آزاد کردن
اشیایی که
disposable
هستند
استفاده از
using
است.
using (someObject = new SomeClass())
{
}
|
کد بالا پس از
کامپایل به
فرمی تبدیل میشود
که انگار به
شکل زیر نوشته
شده بود.
someObject = new SomeClass();
try
{
}
finally
{
if(someObject != null)
someObject.Dispose();
}
|
مثلا در زیر
روش استفاده
از
using
برای باز کردن
یک فایل
و خواندن از آن نشان
داده شده است.
using (StreamReader sr = new StreamReader("file.txt"))
{
}
|
1 نظر:
با سلام
توضیحاتتون خیلی کمک کرد. یک pictureBox داشتم که بعد از dispose کردن عکسش، عکس رو که یک فایل بود رها نمیکرد. وقتی GC.collect(); رو اضافه کردم، درست شد.
ممنون
ارسال یک نظر
جهت نمایش صحیح آدرس سایتتان، حتما قبل از آدرس //:http را درج کنید.