#csharp_for_beginners
یکی از قابلیتهای جذابی که قرار است به سیشارپ ۱۵ اضافه شود، Union Types است. این ویژگی سالهاست در زبانهایی مانند TypeScript وجود دارد و به توسعهدهندگان اجازه میدهد نوعی تعریف کنند که بتواند یکی از چند نوع مشخص را در خود نگه دارد.
فرض کنید میخواهیم مجموعهای از حیوانات خانگی را نگهداری کنیم:
List<object> pets =
[
new Dog("Fido"),
new Cat("tabby"),
new Bird { Species = "crow" }
];
در این مثال از List<object> استفاده کردهایم چون اشیاء موجود در لیست از انواع مختلفی هستند.
اما این راهکار یک مشکل اساسی دارد؛ هر نوع شیء دیگری نیز میتواند وارد این لیست شود:
pets.Add(new Shark(300));
در حالی که کوسه (Shark) اصلاً جزو حیوانات خانگی موردنظر ما نیست.
برای پردازش این دادهها معمولاً از Pattern Matching استفاده میکنیم:
var description = pet switch
{
Dog dog => dog.Name,
Cat(var adjective) => $"A {adjective} cat",
Bird { Species: var species } => $"A {species}"
};
اما کامپایلر هیچ اطلاعی ندارد که چه نوعهایی مجاز هستند و چه نوعهایی نباید در این مجموعه قرار بگیرند.
راهحل: Union Types در سیشارپ ۱۵ میتوان یک Union تعریف کرد:
union Pet(Dog, Cat, Bird);
این تعریف به کامپایلر اعلام میکند که نوع Pet فقط میتواند یکی از این سه نوع باشد:
اکنون میتوان لیست را به شکل زیر تعریف کرد:
List<Pet> pets =
[
new Dog("Fido"),
new Cat("tabby"),
new Bird { Species = "crow" }
];
در این حالت اگر بخواهیم یک Shark به لیست اضافه کنیم، کامپایلر خطا خواهد داد؛ چون Shark جزو اعضای Union نیست.
یکی از مزیت های مهم Union Types این است که میتوان مجموعهای مشخص از انواع مرتبط را تحت یک نوع واحد تعریف کرد.
به همین دلیل هنگام استفاده از Switch Expression میتواند بررسی کند که آیا تمام حالتها پوشش داده شدهاند یا خیر.
برای مثال:
var description = pet switch
{
Dog dog => dog.Name,
Cat(var adjective) => $"A {adjective} cat",
Bird { Species: var species } => $"A {species}"
};
اگر یکی از انواع تعریفشده در Union را فراموش کنیم، کامپایلر هشدار نمایش خواهد داد.
این موضوع باعث میشود بسیاری از خطاها قبل از اجرای برنامه شناسایی شوند.
علاوه بر مشخص کردن انواع مجاز، میتوان رفتارها و قابلیتهای مشترک را نیز مستقیماً درون خود Union پیادهسازی کرد.
برای مثال:
union Pet(Dog, Cat, Bird)
{
public string Description => this switch
{
Dog dog => dog.Name,
Cat(var adjective) => $"A {adjective} cat",
Bird { Species: var species } => $"A {species}"
};
}
در این مثال ویژگی Description مستقیماً داخل Union تعریف شده است.
بنابراین هر شیء از نوع Pet میتواند توضیح مناسب خود را برگرداند، بدون اینکه نیاز باشد این منطق در قسمتهای مختلف برنامه تکرار شود.
این ویژگی باعث میشود کدها تمیزتر و نگهداری آنها سادهتر شود.
تعریف Union با Attributeعلاوه بر سینتکس اختصاصی Union، روش دیگری نیز برای تعریف Unionها در نظر گرفته شده است که مبتنی بر Attribute است.
برای مثال:
[Union]
struct Result<T>
{
}
چرا Attribute-Based Union اهمیت دارد؟
این روش دو مزیت مهم دارد.
۱. بهبود کارایی (Performance)
بدون استفاده از union ها در برخی شرایط باعث Boxing میشود.
در سناریوهایی که عملکرد برنامه اهمیت زیادی دارد و یک قطعه کد بارها و بارها اجرا میشود، استفاده از یک پیادهسازی کاستوم میتواند به کاهش هزینههای اضافی و بهبود کارایی کمک کند.
برای مثال اگر Union شما از چند Struct کوچک تشکیل شده باشد، جلوگیری از Boxing میتواند تأثیر قابل توجهی روی عملکرد برنامه داشته باشد.
۲. ساخت Unionهای کاستوم با کمترین زحمت قبل از معرفی Union Types، بسیاری از توسعهدهندگان تلاش میکردند با استفاده از کلاسهای پایه، اینترفیسها یا حتی قراردادهای مستندسازی و کامنتها، ساختاری شبیه Union ایجاد کنند.
این روشها معمولاً پیچیده بودند و پشتیبانی مستقیمی از سوی کامپایلر نداشتند.
اما اکنون با استفاده از یک Attribute ساده میتوان همان رفتار را به شکل رسمی و استاندارد پیادهسازی کرد.
🔗 ویدئو را در این لینک میتوانید میتوانید مشاهده کنید.
⁉️ برای بحث و تبادل نظر فنی در مورد این پست، نظرات خود را با ما در قسمت کامنتها به اشتراک بگذارید.
کانال تلگرام:
@SoftwarePhilosophy
______