ساخت کلاس Collection سفارشی در سی شارپ

دوشنبه 4 اردیبهشت 1396

این مقاله نشان می دهد که چگونه می توان یک کلاس collection اختصاصی ایجاد کرد. در این آموزش روند ساخت یک کلاس collection به صورت گام به گام توضیح داده شده است.

ساخت کلاس Collection سفارشی در سی شارپ

مقدمه

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

پیش نیاز ها

پیش نیاز لازم برای یادگیری ساخت یک کلاس سفارشی، در حد آشنایی با کد زدن در C# است. در این مقاله از برنامه کنسولی با نام CollectionClassConcept استفاده می کنیم که شامل کلاس خالی با نام CollectionClass است. ما از تنظیمات اولیه خود ویژوال استدیو برای این آموزش استفاده می کنیم.

مرحله 1

در نظر داشته باشید که ما یک کلاس با نام CollectionClass داریم که فعلا هنوز خالی است. کلاس CollectionClass را که در متد اصلی Program.cs فراخوانی کنید و از آن یک نمونه بسازید.

CollectionClass 

namespace CollectionClassConcept  
{  
  public class CollectionClass  
  {  
  
  }  
}  

Program.cs 

using System;  
  
namespace CollectionClassConcept  
{  
  class Program  
  {  
    static void Main(string[] args)  
    {  
      CollectionClass collectionClass=new CollectionClass();  
      foreach (string  collection in collectionClass)  
      {  
        Console.WriteLine(collection);  
      }  
    }  
  }  
} 

وقتی که برنامه کامپایل شد، برنامه منجر به ارورهایی  که در زیر نشان داده شده می شود.



Error

foreach statement cannot operate on variables of type 'CollectionClassConcept.CollectionClass' because 'CollectionClassConcept.CollectionClass' does not contain a public definition for 'GetEnumerator'

مرحله 2

همانطور که در خطا پیشنهاد داده شده است، کلاس باید یک متد GetEnumerator داشته باشد. پس یک متد با نام GetEnumerator() به کلاس CollectionClass اضافه می کنیم.

CollectionClass 

namespace CollectionClassConcept  
{  
  public class CollectionClass  
  {  
    public int GetEnumerator()  
    {  
        
    }  
  }  
}  

Program.cs

using System;  
  
namespace CollectionClassConcept  
{  
  class Program  
  {  
    static void Main(string[] args)  
    {  
      CollectionClass collectionClass=new CollectionClass();  
      foreach (string  collection in collectionClass)  
      {  
        Console.WriteLine(collection);  
      }  
    }  
  }  
} 

وقتی برنامه را کامپایل می کنیم، با خطاهایی که در پایین نمایش داده شده است مواجه می شویم.

Error

CS0161 'CollectionClass.GetEnumerator()': not all code paths return a value

CS0117 'int' does not contain a definition for 'Current'

CS0202 foreach requires that the return type 'int' of 'CollectionClass.GetEnumerator()' must have a suitable public MoveNext method and public Current property

در کدی که بالا هست، foreach تلاش می کند که متد GetEnumertaor را اجرا کند اما نمی تواند، چرا که یک متد MoveNext و یک property با نام Current مورد نیاز است. علاوه بر این، انتظار می رود که این متد هر خروجی به غیر از integer تولید کند.


مرحله 3

در این مرحله می توان یک کلاس جدید با نام CollectionEnumerator ساخت که یک اینترفیس با نام Ienumerator ایجاد می کند و خروجی آن یک نمونه از متد  GetEnumerator در کلاس CollectionClass است.

CollectionEnumerator 

using System.Collections;  
  
namespace CollectionClassConcept  
{  
  public class CollectionEnumerator : IEnumerator  
  {  
  }  
} 

CollectionClass 

    using System.Collections;  
    namespace CollectionClassConcept  
    {  
      public class CollectionClass  
      {  
        public IEnumerator GetEnumerator()  
        {  
          return new CollectionEnumerator();  
        }  
      }  
    }   

Program.cs

    using System;  
      
    namespace CollectionClassConcept  
    {  
      class Program  
      {  
        static void Main(string[] args)  
        {  
          CollectionClass collectionClass=new CollectionClass();  
          foreach (string  collection in collectionClass)  
          {  
            Console.WriteLine(collection);  
          }  
        }  
      }  
    }  

Error

'CollectionEnumerator' does not implement an interface member 'IEnumerator.Current'.

'CollectionEnumerator' does not implement an interface member 'IEnumerator.MoveNext()'.

'CollectionEnumerator' does not implement an interface member 'IEnumerator.Reset()'.

بنابراین، مشخص است که اینترفیس IEnumerator دارای کلاس CollectionEnumerator است و شامل سه متد است که برای پیاده سازی کلاس های فرزند الزامی هستند.

مرحله 4


حالا وقت ساخت این متدها در کلاس CollectionEnumerator و مشاهده نتیجه است.

CollectionEnumerator 


    using System.Collections;  
      
    namespace CollectionClassConcept  
    {  
      public class CollectionEnumerator : IEnumerator  
      {  
        public bool MoveNext()  
        {  
          System.Console.WriteLine("Inside MoveNext Method");  
          return true;  
        }  
      
        public void Reset()  
        {  
          System.Console.WriteLine("Inside Reset Method");  
        }  
      
        public object Current  
        {  
          get  
          {  
            System.Console.WriteLine("Inside Current Property");  
            return "Current Property";  
          }  
        }  
      }  
    }   

CollectionClass 

using System.Collections;  
namespace CollectionClassConcept  
{  
  public class CollectionClass  
  {  
    public IEnumerator GetEnumerator()  
    {  
      return new CollectionEnumerator();  
    }  
  }  
} 

Program.cs 

using System;  
  
namespace CollectionClassConcept  
{  
  class Program  
  {  
    static void Main(string[] args)  
    {  
      CollectionClass collectionClass=new CollectionClass();  
      foreach (string  collection in collectionClass)  
      {  
        Console.WriteLine(collection);  
      }  
    }  
  }  
} 

وقتی برنامه را اجرا می کنیم، با اروری مواجه نمی شویم اما صفحه کنسول به صورت بی نهایت یک خط نمایش می دهد.

چه چیزی باعث این اتفاق می شود که خروجی ما به صورت بی نهایت نمایش داده شود؟ در کلاس CollectionClass متد GetEnumerator توسط حلقه foreach فراخوانی می شود. در اینجا انتظار می رود که این متد GetEnumerator یک خروجی از نوع IEnumerator بدهد که این تکرار یا شمارش بتواند انجام شود. بعد از آن متد MoveNext توسط خروجی ایی که از نمونه سازی از کلاس CollectionEnumerator ایجاد شده است فراخوانی می شود، بنابر این در این مورد MoveNext یک مقدار درست بر میگرداند که این مقدار به طور ضمنی قابل خواندن است و در بازگشت خصوصیت Current را فراخوانی می کند تا داده را دریافت کند. وقتی خصوصیت Current فراخوانی شد، نوشته " Inside Current Property" چاپ می شود و گرفتن خصوصیات جانبی یک property همانگونه که تعریف شده است مقدار " Current Property" را چاپ می کند. حالا MoveNext دوباره فراخوانی می شود و به ازای هر نمونه تعریف شده ما، مقدار True برگشت داده می شود، پس این اتفاق برای برنامه می افتد که خروجی برنامه برای بینهایت بار نمایش داده می شود. در نمونه MoveNext مقدار false موقعی برگشت داده می شود که داده ایی باقی نمانده باشد و حلقه در اینجا متوقف می شود.



مرحله 5

CollectionEnumerator 


    using System;  
    using System.Collections;  
    using System.Collections.Generic;  
      
    namespace CollectionClassConcept  
    {  
      public class CollectionEnumerator : IEnumerator  
      {  
        public List<string> StringList=new List<string>(4) { "Value One", "Value Two", "Value Three", "Value Four", "Value Five" };  
        public int Counter = -1;  
      
        public bool MoveNext()  
        {  
          Counter++;  
          Console.WriteLine("Inside MoveNext Method : " + Counter);  
          return Counter != 5;  
        }  
      
        public void Reset()  
        {  
          Console.WriteLine("Inside Reset Method");  
        }  
      
        public object Current  
        {  
          get  
          {  
            Console.WriteLine("Inside Current Property : " + StringList[Counter]);  
            return StringList[Counter];  
          }  
        }  
      }  
    }   

CollectionClass


    using System.Collections;  
    namespace CollectionClassConcept  
    {  
      public class CollectionClass  
      {  
        public IEnumerator GetEnumerator()  
        {  
          return new CollectionEnumerator();  
        }  
      }  
    }   

Program.cs 


    using System;  
      
    namespace CollectionClassConcept  
    {  
      class Program  
      {  
        static void Main(string[] args)  
        {  
          CollectionClass collectionClass=new CollectionClass();  
          foreach (string  collection in collectionClass)  
          {  
            Console.WriteLine(collection);  
          }  
        }  
      }  
    }   

Output

در کلاس CollectionEnumerator یک generic List از نوع رشته ایجاد شده است. همچنین یک آرایه یا ArrayList ساخته شده است. در این مورد ما از یک لیست با 5 مقدار با مقدارهای  "Value One"،"Value Two"،"Value Three"،"Value Four" و Value Five"" ایجاد می کنیم. یک متغیر شمارنده وجود دارد که مقدار اولیه آن -1 داده شده است. هر دفعه که متد MoveNext فراخوانی می شود، مقدار شمارنده به علاوه عدد 1 می شود، در حال حاضر ما طول لیست را 5 مشخص کرده ایم و اگر این مقدار از 5 بیشتر شود، مقدار false بازگشت داده می شود. بنابر این متغیر counter تعداد دفعات اجرا شدن متد MoveNext را بررسی می کند. هر زمان که متد MoveNext مقدار true برگرداند، خصوصیت Current مقدار رشته داخل لیست تعریف شده را دریافت می کند. ایندکس StringList با توجه به مقدار متغیر current مشخص می شود. در یک روش مشابه، می توان تکرار از طریق لیست را به کمک طول لیست یا آرایه انجام داد.

 
مرحله 6

در این مرحله به ساخت یک کلاس collection با طول متغیر می پردازیم. ما از عدد 5 که برای مثال قبل بود استفاده می کنیم. پس پیاده سازی ما به صورت داینامیک است که ورودی ها را گرفته و پس از محاسبه طول به نمایش خروجی می پردازد.

CollectionEnumerator 


    using System;  
    using System.Collections;  
    using System.Collections.Generic;  
    using System.Linq;  
      
    namespace CollectionClassConcept  
    {  
      public class CollectionEnumerator : IEnumerator  
      {  
        public List<string> StringList;  
        public int Counter = -1;  
      
        public CollectionEnumerator(string parameter)  
        {  
          StringList = parameter.Split(' ').ToList();  
        }  
      
        public bool MoveNext()  
        {  
          Counter++;  
          Console.WriteLine("Inside MoveNext Method : " + Counter);  
          return Counter != StringList.Count;  
        }  
      
        public void Reset()  
        {  
          Console.WriteLine("Inside Reset Method");  
        }  
      
        public object Current  
        {  
          get  
          {  
            Console.WriteLine("Inside Current Property : " + StringList[Counter]);  
            return StringList[Counter];  
          }  
        }  
      }  
    }   

CollectionClass 


    using System.Collections;  
    using System.Collections.Generic;  
      
    namespace CollectionClassConcept  
    {  
      public class CollectionClass  
      {  
        private string _parameter;  
      
        public CollectionClass(string parameter)  
        {  
          _parameter = parameter;  
        }  
        public IEnumerator GetEnumerator()  
        {  
          return new CollectionEnumerator(_parameter);  
        }  
      }  
    }   

Program.cs


    using System;  
    using System.Security.AccessControl;  
      
    namespace CollectionClassConcept  
    {  
      class Program  
      {  
        static void Main(string[] args)  
        {  
          CollectionClass collectionClass=new CollectionClass("We know what is a collection class.");  
          foreach (string  collection in collectionClass)  
          {  
            Console.WriteLine(collection);  
          }  
          Console.ReadLine();  
        }  
      }  
    }   

توضیح دادن کدهایی که در بالا نوشته شده بسیار ساده و واضح است. در متد اصلی کلاس Program، وقتی یک نمونه CollectionClass ساخته می شود ما یک رشته به عنوان پارامتر به کلاس ارسال می کنیم برای اینکه در نظر داشته باشیم که این کلاس دارای یک سازنده پارامتر دار است که یک رشته ورودی می گیرد. بنابراین سازنده کلاس CollectionClass ابتدا فراخوانی می شود و پارامتر ورودی را داخل متغیر _parameter نگه می دارد. حالا حلقه foreach به فراخوانی GetEnumerator می پردازد که باعث ساخته شدن نمونه از کلاس CollectionEnumerator می شود و _parameter  به عنوان یک پارامتر به سازنده کلاس CollectionEnumerator یک سازنده پارامتر دار است ارسال می شود. به ازای هر دفعه ایی که کلاس CollectionEnumerator ساخته می شود، به محض اینکه سازنده کلاس فراخوانی شود، پارامتر به کمک متد Split بر اساس فاصله موجود در رشته، تجزیه میشود. می توان برای تجزیه رشته از کاراکتر های مختلفی استفاده کرد، می توان آرایه ایی از جدا کننده ها داشت ولی فعلا ما از کاراکتر فاصله به عنوان جدا کننده استفاده می کنیم. ما این آرایه تولید شده از تجزیه پارامتر را به یک List از نوع رشته تبدیل می کنیم و به کمک آن StringList را که در کلاس تعریف شده است، مقدار دهی می کنیم. حالا برای اینکه شمارش ما مستقل از یک طول ثابت باشد، ما از یک مقدار ثابت برای طول استفاده نمی کنیم در واقع این مقدار در سازنده کلاس تعیین می شود. بنابراین متد MoveNext فقط وقتی که شمارنده با تعداد اعضای لیست یکسان نباشد مقدار false بر می گرداند. و در آخر ما یک کلاس collection اختصاصی که به ازای هر پیاده سازی و تکرار کلمات داخلی یک رشته را مشخص می کند داریم. همچنین شما می توانید آن را با توجه به نیازمندی های خودتان، شخصی سازی کنید.

نتیجه گیری

این مقاله مبحث های Collection Class و Collection Object را با جزئیات پوشش داده است. ساخت یک کلاس collection ممکن است یک مقدار پیچیده به نظر بیاید ولی پیچیدگی خاصی ندارد و کمکی بسیار خوبی برای ما در مورد کنترل انواع enumeration هایی است که ما می خواهیم استفاده کنیم.

آموزش سی شارپ

فایل های ضمیمه

برنامه نویسان

نویسنده 3355 مقاله در برنامه نویسان
  • C#.net
  • 2k بازدید
  • 7 تشکر

کاربرانی که از نویسنده این مقاله تشکر کرده اند

در صورتی که در رابطه با این مقاله سوالی دارید، در تاپیک های انجمن مطرح کنید