🧬 وراثت در دنیای جنریکها: ساختن تایپهای پیچیده
تو پست قبلی، با قیدهای جنریک آشنا شدیم. حالا میخوایم ببینیم چطور میتونیم این دو مفهوم قدرتمند، یعنی وراثت و جنریکها، رو با هم ترکیب کنیم تا سلسلهمراتبهای تایپی پیچیده و در عین حال قابل استفاده مجدد بسازیم.
1️⃣ ارثبری از کلاسهای جنریک
یک کلاس جنریک میتونه مثل هر کلاس دیگهای به ارث برده بشه. کلاس فرزند (subclass) در این حالت چند تا انتخاب داره:
باز گذاشتن پارامتر نوعی: 📖
در این حالت، کلاس فرزند هم جنریک باقی میمونه و پارامتر نوعی T رو از پدر به ارث میبره.
class Stack<T> { /* ... */ }
class SpecialStack<T> : Stack<T> { /* ... */ }بستن پارامتر نوعی:
شما میتونید با یه نوع مشخص، پارامتر جنریک کلاس پدر رو ببندید. در این حالت، کلاس فرزند شما دیگه جنریک نیست.
class IntStack : Stack<int> { /* ... */ }معرفی پارامترهای نوعی جدید: کلاس فرزند میتونه علاوه بر پارامترهای پدر، پارامترهای جنریک جدیدی هم برای خودش تعریف کنه.
class List<T> { /* ... */ }
class KeyedList<T, TKey> : List<T> { /* ... */ }2️⃣ الگوی پیشرفته: Self-Referencing Generics 🤯
یکی از الگوهای خیلی قدرتمند و رایج، اینه که یک تایپ، یک اینترفیس جنریک رو با خودش به عنوان پارامتر نوعی، پیادهسازی کنه. این کار برای تعریف قراردادهایی مثل قابلیت مقایسه شدن (<IEquatable<T) عالیه و به شما ایمنی نوع کامل در زمان کامپایل میده.
مثال کتاب (Balloon):
public interface IEquatable<T>
{
bool Equals(T obj);
}
// کلاس Balloon، قرارداد مقایسه شدن با یک Balloon دیگر را پیادهسازی میکند
public class Balloon : IEquatable
{
public string Color { get; set; }
public int CC { get; set; }
public bool Equals(Balloon b)
{
if (b == null) return false;
return b.Color == Color && b.CC == CC;
}
}
این الگو خیلی قدرتمنده و تو قیدهای جنریک هم استفاده میشه:
// این کلاس جنریک، فقط تایپهایی رو به عنوان T قبول میکنه
// که خودشون قابل مقایسه با خودشون باشن!
class Foo<T> where T : IComparable<T> { /* ... */ }
🤔 حرف حساب و تجربه شما
ترکیب وراثت و جنریکها به شما اجازه میده ساختارهای تایپی بسیار قدرتمند و انعطافپذیری طراحی کنید که اساس خیلی از کتابخانهها و فریمورکهای مدرن داتنت هستن.
🔖 هشتگها:
#CSharp #DotNet #OOP #Generics