منو

قابلیت Generics(قسمت سی و یکم)

قابلیت Generics
لیست جلسات
DatoosTech img Course

قسمت پنجم

دستورات کنسول و متغیرها

DatoosTech img Course

قسمت ششم

نوع های داده اولیه

DatoosTech img Course

قسمت هفتم

عملگرهای زبان سی شارپ

DatoosTech img Course

قسمت هشتم

دستورات کنترلی if else

DatoosTech img Course

قسمت نهم

دستورات کنترلی switch

DatoosTech img Course

قسمت دهم

حلقه for

DatoosTech img Course

قسمت دوازدهم

حلقه while

DatoosTech img Course

قسمت دوازدهم

آرایه ها و دستور foreach

DatoosTech img Course

قسمت سیزدهم

متدها در زبان سی شارپ (بخش اول)

DatoosTech img Course

قسمت چهاردهم

متدها در زبان سی شارپ (بخش دوم)

DatoosTech img Course

قسمت پانزدهم

مقدمه برنامه نویسی شی گرا

DatoosTech img Course

قسمت شانزدهم

کلاس، اشیاء و فضاهای نام

DatoosTech img Course

قسمت هفدهم

فیلدها و رفتارها

DatoosTech img Course

قسمت هجدهم

Property ها

DatoosTech img Course

قسمت نوزدهم

سازنده ها و روش های ایجاد اشیاء

DatoosTech img Course

قسمت بیستم

وراثت یا Inheritance

DatoosTech img Course

قسمت بیست و یکم

آشنایی با مفهوم Polymorphism

DatoosTech img Course

قسمت بیست و دوم

کلاس های abstract و sealed

DatoosTech img Course

قسمت بیست و سوم

سازنده ها در وراثت، کلمه کلیدی protected و فیلدهای readonly

DatoosTech img Course

قسمت بیست و چهارم

کلاس های static و partial و Extension Method ها

DatoosTech img Course

قسمت بیست و پنجم

Reference Types و Value Types

DatoosTech img Course

قسمت بیست و ششم

آشنایی با کاربرد interface ها

DatoosTech img Course

قسمت بیست و هفتم

مفاهیم Inversion of Control و Dependency Injection

DatoosTech img Course

قسمت بیست و هشتم

Type Casting و انواع Cast ها

DatoosTech img Course

قسمت بیست و نهم

operator overloading و تعریف cast ها

DatoosTech img Course

قسمت سی ام

مفاهیم boxing و unboxing

DatoosTech img Course

قسمت سی و یکم

قابلیت Generics

DatoosTech img Course

قسمت سی و دوم

نکات تکمیلی Generic ها، کلاس List و کلاس Dictionary

DatoosTech img Course

قسمت سی و سوم

استثناء ها (Exceptions) و مدیریت خطاها

مطالب این جلسه
همزمان با بزرگ تر شدن پروژه ای که در حال کار کردن بر روی آن هستید، باید تکنیک هایی را در کد نویسی استفاده کنید که به شما اجازه استفاده مجدد از کدهای نوشته شده را می دهند. یکی از روش های استفاده مجدد از کدهای موجود در موقعیت های مختلف استفاده از قابلیت Generic ها می باشد. این قابلیت به شما اجازه می دهد تا نوع Data Type فیلد ها، خصوصیات و ... برای کلاس ها را زمان ساختن شئ از روی کلاس مشخص کنید. دوستانی که با زبان ++C آشنا هستند، قابلیت Generics در زبان سی شارپ، معادل قابلیت Template ها در زبان ++C است. قابلیت Generics از نسخه 2 به زبان سی شارپ اضافه شد.

ابتدا بیایید ببینیم دنیای بدون Generic ها در زبان سی شارپ چگونه است؟ فرض کنید کلاسی تعریف می کنیم که یک مقدار از نوع int رو داخل خودش نگهداری می کنه:

 
        
public class ValueHolder
{
    public int Value { get; set; }
}
            
        

در قدم بعدی میخواهیم کلاس دیگری برای نگهداری مقادیر از نوع string ایجاد کنیم:

 
        
public class ValueHolder
{
    public string Value { get; set; }
}
            
        

اگر بخواهیم برای هر نوع داده یک کلاس جداگانه ایجاد کنیم، کار جالبی نیست، برای حل این مشکل دو راه حل وجود داره، یکی می توانیم کلاس بالا رو تغییر دهیم و نوع داده خاصیت Value رو از نوع object در نظر بگیریم تا هر مقداری داخلش قرار بگیره:

 
        
public class ValueHolder
{
    public object Value { get; set; }
}
            
        

اما کد بالا مشکلاتی دارد، یکی اینکه با قرار دادن مقادیر از نوع Value Type داخل خصوصیت Value، عملیات Boxing و زمان خواندن مقدار عملیات UnBoxing رخ میدهد که در قسمت قبل گفتیم این دو عملیات باعث کاهش کارآیی برنامه می شوند. همچنین می توان هر مقداری را داخل Value قرار داد که این موضوع مخالف بحث Type Safety می باشد. برای حل این مشکلات، قابلیت Generics به زبان سی شارپ اضافه شد. مثال بالا رو با Generic ها پیاده سازی می کنیم و سپس توضیحات لازم رو خواهیم داد:

 
        
public class ValueHolder<T>
{
    public T Value { get; set; }
}
            
        

همانطوری که ملاحظه می کنید، در مقابل نام کلاس در میان <>، کاراکتر T نوشته شده، در حقیقت کاراکتر T در اینجا، بیانگر یک جایگاه برای یک متغیر می باشد که در داخل کلاس، به جای نوع داده بای خصوصیت کاراکتر T نوشته شده، در حقیقت ما در کد بالا گفتیم که کلاس ValueHolder یک جایگاه برای نوع داده در نظر می گیرد که ما نام T را برای آن انتخاب کردیم (انتخاب این نام کاملاً اختیاری است، اما بهتر است هر نامی که انتخاب می شود ابتدای آن با کاراکتر T که مخفف Type است شروع شود)، سپس در داخل کلاس و قسمت هایی که قصد داریم نوع داده را زمان ساخت کلاس مشخص کنیم، به جای خود Data Type، کاراکتر T را می نویسیم که در کد بالا برای خصوصیت Value کاراکتر T نوشته شده. در قدم بعدی می بایست از روی کلاس ValueHolder یک شئ بسازیم، اما ساختن شئ در اینجا با حالت عادی تفاوت دارد، برای ساختن شئ از روی کلاس های Generic به صورت زیر عمل می کنیم:

 
        
ValueHolder<int> value = new ValueHolder<int>();
value.Value = 12;
            
        

همانطور که مشاهده می کنید، در مقابل نام کلاس در زمان ساخت شئ، داخل <> نام Data Type ای را که می خواهیم جایگزین کاراکتر T شود می نویسیم، در کد بالا، خصوصیت Value از نوع int تعریف می شود. علاوه بر نوع داده int، می توانیم هر نوع داده ای را زمان ساخت شئ مشخص کنیم:

 
            
                ValueHolder<string> stringHolder = new ValueHolder<string>();
                stringHolder.Value = "DatoosTech.com";
            
        

دقت کنید، زمانی که نوع T را برای مثال، string در نظر گرفتید، دیگر نمی توانید داخل خصوصیت Value، مقادیری غیر از string قرار دهید، مانند اینکه کلاس ValueHolder نوع داده خصوصیت Value را از نوع string در نظر گرفته است.

شما می توانید علاوه بر یک جایگاه برای نوع داده Generic، چندین جایگاه تعریف کنیم. مثال:

 
        
public class MultipleGeneric<T1, T2, T3>
{
    public T1 Value1 { get; set; }
    public T2 Value2 { get; set; }
    public T3 Value3 { get; set; }
}
            
        

و زمان ساختن شئ از روی کلاس، به صورت زیر عمل می کنید:

 
        
MultipleGeneric<int, string, decimal> values = new MultipleGeneric<int, string, decimal>();
values.Value1 = 12;
values.Value2 = "DatoosTech.com";
values.Value3 = 2.5m;
            
        

شما علاوه بر اینکه می توانید کلاس ها را به صورت Generic تعریف کنید، امکان تعریف متدها را نیز به صورت Generic دارید. برای مثال، در کد زیر ما یک متد Generic تعریف کردیم که نوع پارامتر های ورودی در زمان صدا زدن متد مشخص می شوند:

 
        
public TOut DoSomething<TOut, TParam1, TParam2>(TParam1 param1, TParam2 param2)
{
    return default(TOut);
}
            
        

در کد بالا یه جایگاه TOut و TParam1 و TParam2 در نظر گرفتیم و در تعریف متد از آنها استفاده کردیم. حالا زمان فراخوانی متد به صورت زیر عمل می کنیم:

 
        
string result = DoSomething<string, int, int>(12, 20);
            
        

اما به یک نکته توجه کنید، در داخل بدنه متد، از کلمه کلیدی default مانند یک متد استفاده شده که داخل پرانتز، نام جایگاه TOut را نوشتیم، کلمه کلیدی TOut، مقدار پیش فرض را برای نوع داده مشخص شده برای جایگاه Generic بر می گرداند. در کد بالا، نوع داده string برای TOut مشخص شده و عبارت default مقدار پیش فرض نوع داده string را بر میگرداند.

برای استفاده از نوع داده generic برای متدها در کلاس ها، می توانید از جایگاه های تعریف شده برای کلاس نیز استفاده بکنید:

 
        
public class GenericType<T>
{
    public void DoSomething(T param)
    {
            
    }
}
            
        

در کد بالا، همانطور که مشاهده می کنید، برای پارامتر ورودی متد، از جایگاه T که در کلاس تعریف کردیم استفاده کردیم. در این قسمت با مباحث اولیه generic ها آشنا شدیم، در قسمت بعدی یکسری نکات کوچک راجع به generic ها را بررسی کرده، با کلاس List آشنا شده و به بررسی Constraint ها در Generics خواهیم پرداخت.