کلمات انگلیسی Mutable و Immutable به ترتیب به معنی تغییر پذیر و تغییر ناپذیر هستن
توی زبان برنامه نویسی سی شارپ هم همین مفهوم رو دارن این یعنی انواع قابل تغییر آنهایی هستند که مقدار داده آنها پس از ایجاد نمونه قابل تغییر است اما انواع غیرقابل تغییر آنهایی هستند که مقدار داده آنها پس از ایجاد نمونه قابل تغییر نیستند. وقتی مقدار اشیاء قابل تغییر را تغییر می دهیم، مقدار در همان حافظه تغییر می کند. اما در نوع تغییرناپذیر، حافظه جدید ایجاد می شود و مقدار اصلاح شده در حافظه جدید ذخیره می شود.
به طور مثال رشته ها Immutable هستند، به این معنی که ما هر بار به جای تغییر بر روی حافظه موجود، حافظه جدیدی تخصیص میدیم بهشون. به عبارت دیگه هر زمان که مقداری از رشته موجود را تغییر میدهیم، یعنی یک خونه جدید از حافظه رو بهش اختصاص میدیم که به آن رشته تغییر یافته اشاره میکند و مورد قبلی اصطلاحا Dereference میشود. (اگر این بخش رو متوجه نشدید پیشنهاد میکنم به این لینک مراجعه کنید)
خب اوکی ! یه خونه جدید اختصاص بده چی میشه مگه ؟
عرض کنم خدمت حضور انورتون که اگر شما به طور مداوم مقدار یک رشته رو تغییر بدید تعداد خونه های Dereferenced توی حافظه زیاد میشن و همینطوری منتظر Garbage collector میمونن تا بره سراغشون و اون خونه هارو آزاد(Dispose) کنه و خب این برامون Performance issue ایجاد میکنه که خب این بده :)
string str = string.Empty; for (int i = 0; i < 1000; i++) { str += "Allocated" }
الان توی تکه کد باکس بالا دقیقا اون اتفاقی که توضیح دادم افتاده. به طور کلی ایده جالبی نیست انجام این کار
برای همین در سی شارپ کلاس StringBuilder رو داریم که از نوع Mutable هستش اینگونه بنویسیم بهتره :)
StringBuilder strB = new StringBuilder(); for (int i = 0; i < 10000; i++) { strB.Append("Modified"); }
کلاس StringBuilder متود های دیگه ایی مثل Remove , Insert ,Replace و .. دارد که اگر براتون جذابه میتونید توی وبسایت مایکروسافت بیشتر راجع بهش بخونید.
استفاده شده از پکیج BenchmarkDotNet
اشیاء Immutable از آنجایی که State شی تغییر نمیکنه، Thread Safety میشوند ، بنابراین مهم نیست که چند رشته از آن به طور همزمان استفاده کنن، State آن تغییر نمیکند. اگر هر رشته ای به شی با State تغییر یافته نیاز داشته باشه، باید یک شی جدید ایجاد کنه. بنابراین دیگه synchronization issue. وجود نداره. در نتیجه ، تغییرناپذیری Thread safety را تضمین می کند.
وقتی State یک شیء Immutable را تعریف وکردیم ، هیچ راهی وجود نداره که بدون اطلاع شما توسط هیچ رشته یا فرآیند پس زمینه تغییر کنه. به این ترتیب برنامه هایی که دارای اشیاء تغییرناپذیر هستن از امنیت بالایی برخوردارن. چون میدونیم که اشیاء شما قبل از وارد شدن به Invalid State ایمن می مانند.
کپسوله سازی بخش اساسی برنامه نویسی شی گراست. بدوناون OOP بی معنیه. با کمک اشیاء تغییرناپذیر می تونیم در حین پاس دادن مقدار به روش های مختلف کپسولاسیون بهتری داشته باشیم و مطمئن باشیم که حالت آنها تغییر نمیکند.
هر زمان که هنگا خطایی رخ دهد، می دانیم که اشیاء Immutable تغییر نخواهند کرد. به همین خطر اونها نمیتونن عامل ایجاد شدن باگ باشند. بنابراین، در حین debug ، از کدی که دارای یک شی غیرقابل تغییر هست صرفنظر میکنیم و debugمون ساده و سریع میشه.
تست یک Code Base با اشیاءImmutable ساده تره چون کد شما دارای Side Effect کمتریه که به طور خودکار منجر به مسیرهای گیج کننده(confusing code) کمتری میشه که در نهایت بررسیشون ساده تره.
به نظر من این نوع کنار خوبیای که داره یه بدی بیشتر نداره اونم ابتدای مقاله توضیح دادم
با استفاده از این نوع شما از حافظه استفاده بیشتری میکنید و به عبارتی زحمت بیشتری به garbage collector سی شارپ میدین.
وقتی که از اشیاء Mutable استفاده می کنیم میتونیم State اونا رو به طور مکرر تغییر دهیم بدون اینکه آنها را دوباره تعریف کنیم.
استفاده از اشیاء Mutable روش خوبی برای مدیریت حافظه است چونکه هر زمان که به یک شی با State تغییر یافته نیاز داشتیم نیازی به ایجاد یک شی جدید نیست همونو عوضش میکنیم.
تا اینجای کار احتمالا متوجه شدید که تا وقتی immutable ها هستن و میتونیم ازشون استفاده کنیم نریم سراغ Mutable ها بهتره که خب این یه سری دلایل داره که خب نوشتم براتون
اون بحث که Immutable ها synchronization issue ندارن رو یادتونه ؟
همونطور که برای Immutable ها مزیت بود اینجا بین Mutable ها عیب محسوب میشه به طوری که state داخلی اشیاء Mutable می تواند توسط هر رشته ای که به آن دسترسی پیدا کند تغییر کند. خب این باعث synchronization issue میشه چون با تعاریفی که داشتیم هیچ دو رشته ای نمی تونن به طور همزمان بهش دسترسی داشته باشن.
وقتی که از اشیاء Mutable استفاده می کنید، نگهداری کد شما دشوار می شود زیرا State اشیاء شما در سرتاسر کد تغییر می کند و توان عملیاتی را کاهش می دهد.
اشیاء Mutable تست و Debug کد شما را دشوار می کنند زیرا مسیرهای کد بیشتر منجر به سردرگمی می شود. Debug نیز دشوار است زیرا State شیء به طور مکرر تغییر می کند.
بقیش بمونه مقاله بعدی ، باشد که رستگار شویم:))