چه زمانی از IEnumerable ، ICollection، IList و List استفاده کنیم؟

IEnumerable

فکر می کنم این سوال که چه زمانی از IEnumerable ، ICollection، IList و List استفاده کنیم، سوال متداولی است که اغلب به درستی پاسخ داده نمی شود. در این مقاله، نه تنها به این سوال پاسخ می دهیم بلکه می خواهیم با ارائه یک سری اطلاعات مباحث را به طور عمیق تری درک کنیم.

اگر اصول استفاده از آن ها را یاد بگیریم، زمانی که مجبور به تصمیم گیری باشیم با اطمینان بیشتری عمل خواهیم کرد.

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

اجازه دهید ابتدا نگاهی به هر یک از این نوع داده ها و مهم تر از آن اعضای آن ها بیندازیم.

IEnumerable

قبل از هر چیز، بسیار مهم است که بدانیم دو اینترفیس مختلف در کلاس کتابخانه ای  پایه .NET تعریف شده است. یک نوع اینترفیس غیرجنریک  IEnumerable و یک اینترفیس جنریک type-safe به صورت IEnumerable<T> وجود دارد.

اینترفیس IEnumerable در فضای نام System.Collections  قرار دارد و فقط شامل تعریف یک متد است. تعریف اینترفیس به شکل زیر می باشد:


public interface IEnumerable

{

IEnumerator GetEnumerator();

{

متد GetEnumerator باید یک شی از جنس کلاسی را برگرداند که اینترفیس IEnumerator را پیاده سازی می کند. در این مقاله به بررسی اینترفیس IEnumerator نخواهیم پرداخت، اما اگر علاقمند هستید که بیشتر درباره این اینترفیس بدانید می توانید به داکیومنت رسمی msdn نگاهی بیندازید.

این مهم است که بدانید کلمه کلیدی foreach در زبان C# با همه انواع داده ای که اینترفیس IEnumerable را پیاده سازی کنند، کار می کند. فقط در C#، foreach با چیزهایی که به صورت مستقیم IEnumerable یا IEnumerable<T> را پیاده سازی نکرده باشند هم کار می کند. شک ندارم که شما بدون نگرانی درباره اینکه چرا و چگونه با این نوع داده ای کار می کند، بارها از کلمه کلیدی foreach استفاده کرده اید.

 

IEnumerable<T>

حال می خواهیم نگاهی به ورژن جنریک و type-safe این اینترفیس با نام IEnumerable<T> که در فضای­نام  System.Collections.Generic قرار دارد.


public interface IEnumerable<out T> : IEnumerable

{

IEnumerator<T> GetEnumerator();

{

همان طور که مشاهده می کنید، اینترفیس IEnumerable<T> از اینترفیس IEnumerable ارث بری می کند. بنابراین یک نوع داده ای که IEnumerable<T> را پیاده سازی می کند، اعضای IEnumerable را هم پیاده سازی می کند.

IEnumerable<T> یک متد GetEnumerator را تعریف می کند که یک شی از جنس کلاسی را برمی گرداند که اینترفیس IEnumerator<T> را پیاده سازی می کند. در این مقاله به بررسی این اینترفیس هم نخواهیم پرداخت، اما اگر علاقمند هستید که بیشتر درباره این اینترفیس بدانید می توانید به داکیومنت رسمی msdn نگاهی بیندازید.

ICollection

همان طور که می توانید حدس بزنید، ICollection نیز دو ورژن دارد که System.Collections.ICollection و نوع جنریک آن System.Collections.Generic.ICollection<T> می باشد.

تعریف اینترفیس ICollection به شکل زیر می باشد:


public interface ICollection : IEnumerable

{

int Count { get; }

bool IsSynchronized { get; }

Object SyncRoot { get; }


void CopyTo(Array array, int index);

}

ICollection نیز از IEnumerable ارث بری می کند. همچنین تمام اعضای اینترفیس IEnumerable نیز در همه کلاس هایی که اینترفیس ICollection را پیاده سازی می کنند، پیاده سازی می شوند.

در حال حاضر به جزییات تعریف متدها و property ها نمی پردازیم. فقط به ذکر این نکته از توضیح رسمی از داکیومنت msdn اکتفا می کنیم:

متدهای size، enumerator و synchronization برای همه کالکشن های غیرجنریک تعریف می شوند.

ICollection<T>

زمانی که به ورژن جنریک ICollection نگاه کنیم، متوجه می شوید که این نوع دقیقا شبیه به نوع غیرجنریک آن نیست.


public interface ICollection<T> : IEnumerable<T>, IEnumerable

{

int Count { get; }

bool IsReadOnly { get; }


void Add(T item);

void Clear();

bool Contains(T item);

void CopyTo(T[] array, int arrayIndex);

bool Remove(T item);

{

توضیح داکیومنت msdn درباره آن:

متدها برای دستکاری کالکشن های جنریک تعریف می شوند.

در واقع، ما متدهای بیشتری برای اضافه، حذف و پاک کردن کالکشن داریم. روش همگام سازی پیاده سازی شده نیز متفاوت است. به نظر من، به این دلیل است که نوع جنریک این اینترفیس با .NET 2.0 معرفی شد، در حالی که نوع غیرجنریک آن در .NET 1.1 معرفی شده بود.

IList

اینترفیس IList نیز دو نوع جنریک و غیرجنریک دارد. ما ابتدا با بررسی اینترفیس IList غیرجنریک شروع می کنیم.

 


public interface IList : ICollection, IEnumerable

{

bool IsFixedSize { get; }

bool IsReadOnly { get; }

Object this[int index] { get; set; }

&nbsp;

int Add(Object value);

void Clear();

bool Contains(Object value);

int IndexOf(Object value);

void Insert(int index, Object value);

void Remove(Object value);

void RemoveAt(int index);

{

اینترفیس IList، ICollection و IEnumerable را پیاده سازی می کند. علاوه بر آن تعریفی برای متدهای اضافه کردن و حذف کردن عناصر و همچنین پاک کردن کالکشن فراهم می کند. همچنین متدهایی برای مدیریت موقعیت عناصر درون کالکشن نیز فراهم می کند. علاوه بر این ها، یک شی indexer در اختیار ما قرار می دهد که به کاربر امکان دسترسی به کالکشن با استفاده از [] را می دهد:


myList[elementIndex]

حالا نگاهی به نسخه جنریک IList می اندازیم.


public interface IList<T> : ICollection<T>, IEnumerable<T>, IEnumerable

{

T this[int index] { get; set; }


int IndexOf(T item);

void Insert(int index, T item);

void RemoveAt(int index);

{

نتیجه گیری

تصویر زیر را مشاهده کنید. اعضای هیچ یک از اینترفیس ها نمایش داده نشده، اما برای یک مرور کلی درباره همه انواع داده ای که بحث کردیم کافی است.

IEnumerable

از کدام نوع باید استفاده کنیم؟

حال که همه اینترفیس ها را بررسی کرده و مورد بحث قرار دادیم، وقت آن رسیده که مشخص کنیم در هر موقعیتی از کدام اینترفیس استفاده نماییم. به طور کلی، بهتر است که تنها به چیزهایی که واقعا نیاز داریم اکتفا کنیم. در ادامه می خواهیم به شما نشان بدهیم که چگونه می توان به سادگی این تصمیم را گرفت و چه چیزی در انتظار شماست اگر این کار را انجام دهید.

اگر ما از یک اینترفیس محدودتری مانند IEnumerable به جای IList استفاده کنیم، کد خود را از breaking change (تغییری در یک قسمت کد که اجرا یا کامپایل قسمت های دیگر کد را با مشکل مواجه می کند) محافظت می کنیم. اگر از IEnumerable استفاده کنیم، فراخواننده متد ما می تواند هر شی ای که اینترفیس IEnumerable را پیاده سازی می کند، فراهم کند. تقریبا همه انواع کالکشن های کلاس کتابخانه ای پایه و علاوه بر آن بسیاری از انواع سفارشی تعریف شده را شامل می شود. کد فراخواننده می تواند در آینده تغییر کند و کد ما به آن سادگی که با استفاده از ICollection و یا بدتر از آن IList ممکن بود دچار شکست شود، با مشکل مواجه نخواهد شد.

اگر ما از یک اینترفیس وسیع تری مانند IList استفاده کنیم، بیشتر در معرض خطر breaking change هستیم. اگر کسی بخواهد متد ما را با یک شی سفارشی تعریف شده فراخوانی کند که فقط IEnumerable را پیاده سازی کرده، کد ما کار نخواهد کرد و در زمان کامپایل با خطا مواجه می شود.

به طور کلی همیشه باید از نوع داده ای استفاده کنیم که تنها به متدهایی که ما واقعا نیاز داریم محدود باشد.

جدول زیر یک جمع بندی کلی درباره اینکه چگونه نوع موردنیاز خود را انتخاب کنید، ارائه می دهد.

اینترفیس سناریو
IEnumerable, IEnumerable<T> تنها چیزی که شما نیاز دارید یک حلقه تکرار روی عناصر یک کالکشن است. تنها به یک دسترسی فقط خواندنی به آن کالکشن نیاز دارید.
ICollection, ICollection<T> می خواهید کالکشن را تغییر دهید یا اندازه آن برای شما مهم است.
IList, IList<T> می خواهید کالکشن را تغییر دهید یا ترتیب و یا محل قرار گیری عناصر در کالکشن برای شما مهم است.
List, List<T> از آنجا که در طراحی شی گرا به جای پیاده سازی براساس انتزاعی سازی عمل می کنیم، شما هرگز نباید عضوی از پیاده سازی های خود را از نوع غیرانتزاعی List داشته باشید.

 

 

  • پسورد: www.mspsoft.com
فاطمه زکایی

فاطمه زکایی هستم. فارغ التحصیل کارشناسی مهندسی نرم افزار، مدت سه سال هست که در زمینه توسعه اپلیکیشن های تحت وب و اندروید و همچنین تولید محتوای تخصصی برنامه نویسی تحت وب و اندروید در مجموعه mspsoft در خدمت شما هستم.

نوشته‌های مرتبط

دیدگاه‌ها

*
*

این سایت از اکیسمت برای کاهش هرزنامه استفاده می کند. بیاموزید که چگونه اطلاعات دیدگاه های شما پردازش می‌شوند.