zkazemi
شنبه 17 مرداد 1388, 10:34 صبح
کلاسهاي generic
کلاسهاي generic, عملياتي را دربرميگيرند که متعلق به يک نوع داده خاص نميباشند. کلاسهاي generic, بيشتر در مجموعههايي مانند ليستهاي پيوندي, (linked list) جداول درهم (hash tableها), پشتهها (stackها), صفها (queueها), درختها و غيره مورد استفاده قرار ميگيرند که عملياتي از قبيل افزودن و حذف کردن عناصر از آنها, بدون درنظر گرفتن نوع داده ذخيره شده, به يک روش, صورت ميگيرند. در بيشتر مواردي که به کلاسهاي مجموعهاي نياز است, هدف اصلي, بکارگيري مجموعههايي است که در مخزن مجموعههاي .NET Framework 2.0 , ارائه شدهاند. به طور معمول, کلاسهاي generic با استفاده از يک کلاس خاص موجود ايجاد ميشود. به اين ترتيب که نوع دادهها را يکي يکي تبديل به پارامتر ميکنيد تا زماني که به بهترين توازن در عموميسازي و مصرف برسيد. به هنگام ايجاد کلاسهاي generic, حتماً به موارد زير توجه کنيد:
· کداميک از انواع دادهاي را بايد به پارامتر تبديل نمود؟
به طور معمول, هر چه انواع دادهاي بيشتري را به پارامتر تبديل کنيد, کد حاصل, قابل استفادهتر و انعطافپذيرتر خواهد شد. با اين وجود, نيزان بالاي عموميسازي, باعث ميشود که کد شما سختتر توسط ساير برنامهنويسان خوانده يا درک شود.
· در صورت نياز, چه محدوديتهايي در پارامترها اعمال شوند؟
بهتر است تا حدي محدوديتهاي مختلف را اعمال کنيد که باز هم بتوانيد که انواعي را که نياز به کنترل دارند, اداره کنيد. مثلاً اگر بدانيد که قرار است کلاس generic شما, فقط توسط انواع مرجع (reference) مورد استفاده قرار گيرند, بايد محدوديت کلاسي ايجاد کنيد. اين کار, باعث جلوگيري از استفاده ناخواسته از کلاس شما با انواع دادهاي شده, به شما امکان ميدهد که از عملگر as بر روي T استفاده نمود, مقادير null را کنترل کنيد.
· آيا بايد يک يا چند رابط generic پيادهسازي شوند يا خير؟
به عنوان مثال, چنانچه بخواهيد کلاسي را ايجاد کنيد که براي توليد عناصري در يک مجموعه مبتني بر genericها, مورد استفاده قرار ميگيرد, ممکن است نياز داشته باشيد که يک رابط از قبيل Icomparable<T> پيادهسازي شود که در آن, T, نوع کلاس شما است.
قوانين مربوط به پارامترها و محدوديتها, با توجه به توارث و قابليت دسترسي اعضاء, داراي ملزومات و معاني ضمني متعددي از نظر رفتار کلاس generic ميباشند. پيش از ادامه کار, بايد با برخي از اين مفاهيم, آشنا شويد. در يک کلاس generic Node <T>, کد خدماتگير ميتواند يا توسط تعيين يک آرگومان نوع, يک نوع ساخت يافته بسته را (Node<int>) ايجاد کند و يا ميتواند با مشخص نکردن پارامتر نوع مذکور, مثلاً به هنگام تعيين يک کلاس مبتني بر generic, يک نوع ساختيافته باز را (Node<T>) ايجاد نمايد. کلاسهاي generic ميتوانند از کلاسهاي ساخت يافته باز, ساختيافته بسته و يا موجود, به ارث برند:
class BaseNode {}
class BaseNodeGeneric<T> {}
//concrete type
class NodeConceret<T>: BaseNode {}
// closed constructed type
Class NodeClosed<T>: BaseNodeGeneric<int> {}
//open constructed type
Class NodeOpen<T>: BaseNodeGeneric<T> {}
کلاسهاي موجود غير generic, ميتوانند از کلاسهاي اصلي ساختيافته بسته به ارث برند ولي نميتوانند از کلاسهاي ساختيافته باز, يا پارامترهاي بدون نوع (naked type) ارث برند زيرا در زمان اجرا, کد خدماتگير, به هيچ طريقي نميتواند آرگومان نوع خواسته شده را جهت مقداردهي اوليه کلاس اصلي, تأمين کند:
//No error
class Node1: BaseNodeGeneric<int> {}
//Generates an error
//class Node2: BaseNodeGeneric<T> {}
//Generates an error
//class Node3: T {}
کلاسهاي generic که از انواع ساختيافته باز, ارث ميبرند, بايد آرگومانهاي نوع را براي هر پارامتر نوع کلاس اصلي که با کلاس ارث برنده, به اشتراک گذارده نشدهاند, تأمين نمايند:
class BaseNodeMultiple<T, U> {}
//No error
class Node4: BaseNodeMultiple<T, int> {}
//No error
//class Node5: BaseNodeMultiple<T, U> {}
//Generates an error
//class Node6<T>: BaseNodeMultiple<T,U> {}
کلاسهاي generic که از انواع ساختيافته باز, ارث ميبرند, بايد محدوديتهايي را مشخص کنند که يک ابر مجموعه از محدوديتهاي نوع اصلي ميباشند:
class NodeItem<T> where T: System.Icomparable<T>, new() {}
class SpecialNodeItem<T>: NodeItem<T> where T: System.Icomparable<T>, new ()
{}
انواع gerenic ميتوانند از چندين پارامتر نوع و محدوديت, استفاده کنند.
class SuperKeyType<K, V< U>
where U: System.Icomparable<U>
where V: new()
{}
انواع ساختيافته باز و بسته را ميتوان به عنوان پارامترهاي متد به کار برد:
voidSwap<T>(List<T> list1, List<T> list2)
{
//code to swap items
}
Void Swap(List<int> list1, List<int> list2)
{
// code to swap items
}
کلاسهاي generic, ثابت هستند. به عبارت ديگر, چنانچه يک پارامتر ورودي, يک List<BaseClass> را تعيين کند, در صورت تلاش براي ارائه يک List<Derived Class, با يک خطاي زمان کامپال مواجه ميشويد.
کلاسهاي generic, عملياتي را دربرميگيرند که متعلق به يک نوع داده خاص نميباشند. کلاسهاي generic, بيشتر در مجموعههايي مانند ليستهاي پيوندي, (linked list) جداول درهم (hash tableها), پشتهها (stackها), صفها (queueها), درختها و غيره مورد استفاده قرار ميگيرند که عملياتي از قبيل افزودن و حذف کردن عناصر از آنها, بدون درنظر گرفتن نوع داده ذخيره شده, به يک روش, صورت ميگيرند. در بيشتر مواردي که به کلاسهاي مجموعهاي نياز است, هدف اصلي, بکارگيري مجموعههايي است که در مخزن مجموعههاي .NET Framework 2.0 , ارائه شدهاند. به طور معمول, کلاسهاي generic با استفاده از يک کلاس خاص موجود ايجاد ميشود. به اين ترتيب که نوع دادهها را يکي يکي تبديل به پارامتر ميکنيد تا زماني که به بهترين توازن در عموميسازي و مصرف برسيد. به هنگام ايجاد کلاسهاي generic, حتماً به موارد زير توجه کنيد:
· کداميک از انواع دادهاي را بايد به پارامتر تبديل نمود؟
به طور معمول, هر چه انواع دادهاي بيشتري را به پارامتر تبديل کنيد, کد حاصل, قابل استفادهتر و انعطافپذيرتر خواهد شد. با اين وجود, نيزان بالاي عموميسازي, باعث ميشود که کد شما سختتر توسط ساير برنامهنويسان خوانده يا درک شود.
· در صورت نياز, چه محدوديتهايي در پارامترها اعمال شوند؟
بهتر است تا حدي محدوديتهاي مختلف را اعمال کنيد که باز هم بتوانيد که انواعي را که نياز به کنترل دارند, اداره کنيد. مثلاً اگر بدانيد که قرار است کلاس generic شما, فقط توسط انواع مرجع (reference) مورد استفاده قرار گيرند, بايد محدوديت کلاسي ايجاد کنيد. اين کار, باعث جلوگيري از استفاده ناخواسته از کلاس شما با انواع دادهاي شده, به شما امکان ميدهد که از عملگر as بر روي T استفاده نمود, مقادير null را کنترل کنيد.
· آيا بايد يک يا چند رابط generic پيادهسازي شوند يا خير؟
به عنوان مثال, چنانچه بخواهيد کلاسي را ايجاد کنيد که براي توليد عناصري در يک مجموعه مبتني بر genericها, مورد استفاده قرار ميگيرد, ممکن است نياز داشته باشيد که يک رابط از قبيل Icomparable<T> پيادهسازي شود که در آن, T, نوع کلاس شما است.
قوانين مربوط به پارامترها و محدوديتها, با توجه به توارث و قابليت دسترسي اعضاء, داراي ملزومات و معاني ضمني متعددي از نظر رفتار کلاس generic ميباشند. پيش از ادامه کار, بايد با برخي از اين مفاهيم, آشنا شويد. در يک کلاس generic Node <T>, کد خدماتگير ميتواند يا توسط تعيين يک آرگومان نوع, يک نوع ساخت يافته بسته را (Node<int>) ايجاد کند و يا ميتواند با مشخص نکردن پارامتر نوع مذکور, مثلاً به هنگام تعيين يک کلاس مبتني بر generic, يک نوع ساختيافته باز را (Node<T>) ايجاد نمايد. کلاسهاي generic ميتوانند از کلاسهاي ساخت يافته باز, ساختيافته بسته و يا موجود, به ارث برند:
class BaseNode {}
class BaseNodeGeneric<T> {}
//concrete type
class NodeConceret<T>: BaseNode {}
// closed constructed type
Class NodeClosed<T>: BaseNodeGeneric<int> {}
//open constructed type
Class NodeOpen<T>: BaseNodeGeneric<T> {}
کلاسهاي موجود غير generic, ميتوانند از کلاسهاي اصلي ساختيافته بسته به ارث برند ولي نميتوانند از کلاسهاي ساختيافته باز, يا پارامترهاي بدون نوع (naked type) ارث برند زيرا در زمان اجرا, کد خدماتگير, به هيچ طريقي نميتواند آرگومان نوع خواسته شده را جهت مقداردهي اوليه کلاس اصلي, تأمين کند:
//No error
class Node1: BaseNodeGeneric<int> {}
//Generates an error
//class Node2: BaseNodeGeneric<T> {}
//Generates an error
//class Node3: T {}
کلاسهاي generic که از انواع ساختيافته باز, ارث ميبرند, بايد آرگومانهاي نوع را براي هر پارامتر نوع کلاس اصلي که با کلاس ارث برنده, به اشتراک گذارده نشدهاند, تأمين نمايند:
class BaseNodeMultiple<T, U> {}
//No error
class Node4: BaseNodeMultiple<T, int> {}
//No error
//class Node5: BaseNodeMultiple<T, U> {}
//Generates an error
//class Node6<T>: BaseNodeMultiple<T,U> {}
کلاسهاي generic که از انواع ساختيافته باز, ارث ميبرند, بايد محدوديتهايي را مشخص کنند که يک ابر مجموعه از محدوديتهاي نوع اصلي ميباشند:
class NodeItem<T> where T: System.Icomparable<T>, new() {}
class SpecialNodeItem<T>: NodeItem<T> where T: System.Icomparable<T>, new ()
{}
انواع gerenic ميتوانند از چندين پارامتر نوع و محدوديت, استفاده کنند.
class SuperKeyType<K, V< U>
where U: System.Icomparable<U>
where V: new()
{}
انواع ساختيافته باز و بسته را ميتوان به عنوان پارامترهاي متد به کار برد:
voidSwap<T>(List<T> list1, List<T> list2)
{
//code to swap items
}
Void Swap(List<int> list1, List<int> list2)
{
// code to swap items
}
کلاسهاي generic, ثابت هستند. به عبارت ديگر, چنانچه يک پارامتر ورودي, يک List<BaseClass> را تعيين کند, در صورت تلاش براي ارائه يک List<Derived Class, با يک خطاي زمان کامپال مواجه ميشويد.