مقایسه اشیاء با استفاده از رابط های IComparable و IComparer

دو رابط مفید برای مقایسه اشیایی که توسط کاربر تعریف شده اند وجود دارد. این دو رابط IComparable<T> و IComparer<T> می باشند. رابط IComparable<T> در یک کلاس پیاده سازی می شود و اجازه می دهد کلاس یا شی ایجاد شده از کلاس با اشیاء دیگر از همین کلاس مقایسه شوند.IComparer<T> در یک کلاس جداگانه پیاده سازی می شود. هماطور که از نام این رابط پیداست، پیاده سازی آن باعث ایجاد یک کلاس مقایسه پذیر می شود. به این نکته توجه کنید که نسخه های غیر جنریک این دو رابط نیز وجود دارد ولی کار کردن با نسخه های جنریک آنها بسیار راحت تر بوده و شما نیاز به تبدیل برای مقایسه اشیاء ندارید.

 

رابط <IComparable<T

حال به نحوه استفاده از رابط IComparable<T> می پردازیم. در مثال زیر یک کلاس نشان داده شده است که رابط IComparable<T> را پیاده سازی می کند:

   1: public class Person : IComparable<Person>
   2: {
   3:     public string FirstName { get; set; }
   4:     public string LastName { get; set; }
   5:     public int Age { get; set; }
   6: 
   7:     public int CompareTo(Person other)
   8:     {                                 
   9:         if (this.Age > other.Age)     
  10:         return 1;                     
  11:         else if (this.Age < other.Age)
  12:         return -1;                    
  13:         else                          
  14:         return 0;                     
  15:     }                                 
  16: }

وقتی که یک کلاس از رابط IComparable<T> استفاده می کند، لازم است که تنها متد آن یعنی متد CompareTo() را نیز پیاده سازی کند. متد CompareTo() یک مقدار صحیح را بر می گرداند. این متد همچنین یک آرگومان قبول می کند که همان شیی است که قرار است با شی جاری مقایسه شود. در داخل متد CompareTo() در مثال بالا ما سن (age) شخص فعلی را با سن شخص دیگر مقایسه کرده ایم. طبق قرارداد اگر سن شخص مورد نظر ما از سن شخص دیگر بیشتر بود مقداری بزرگتر از صفر، اگر کمتر بود مقداری کمتر از صفر و اگر مساوی بود مقدار صفر برگشت داده می شود. کد زیر پیاده سازی رابط IComparable<T> توسط شی Person را نشان می دهد. برنامه جوانترین و پیرترین شخص را تشخیص می دهد.

  1: public class Program
  2: {
  3:     static void Main(string[] args)
  4:     {
  5:         Person person1 = new Person { FirstName = "John", LastName = "Smith", Age = 21 };
  6:         Person person2 = new Person { FirstName = "Mark", LastName = "Logan", Age = 19 };
  7:         Person person3 = new Person { FirstName = "Luke", LastName = "Adams", Age = 20 };
  8: 
  9:         Person youngest = GetYoungest(person1, person2, person3);
 10:         Person oldest = GetOldest(person1, person2, person3);
 11: 
 12:         Console.WriteLine("The youngest person is {0} {1}.", 
 13:             youngest.FirstName, youngest.LastName);
 14:         Console.WriteLine("The oldest person is {0} {1}.", 
 15:             oldest.FirstName, oldest.LastName);
 16:         Console.ReadKey();
 17:     }
 18: 
 19:     private static Person GetYoungest(Person person1, Person person2, Person person3)
 20:     {
 21:         Person youngest = person1; 
 22: 
 23:         if (person2.CompareTo(youngest) == -1) 
 24:             youngest = person2;
 25: 
 26:         if (person3.CompareTo(youngest) == -1) 
 27:             youngest = person3;
 28: 
 29:         return youngest;
 30:     }
 31: 
 32:     private static Person GetOldest(Person person1, Person person2, Person person3)
 33:     {
 34:         Person oldest = person1; 
 35: 
 36:         if (person2.CompareTo(oldest) == 1) 
 37:             oldest = person2;
 38: 
 39:         if (person3.CompareTo(oldest) == 1) 
 40:             oldest = person3;
 41: 
 42:         return oldest;
 43:     }
 44: }
The youngest person is Mark Logan.
The oldest person is John Smith.

در خطوط 7-5 سه شی Person با مقادیر کاملا اختیاری ایجاد شده است. در خطوط 10-9 متغیرهایی برای نگهداری جوانترین و پیرترین شخص تعریف شده اند. در خط 9 متد GetYoungest() را فراخوانی کرده ایم. این متد در خطوط 19-30 تعریف شده است و سه شخص را که قرار است از لحاظ سنی با هم مقایسه شوند را قبول می کند.

در خط 21 فرض را بر این گذاشته ایم که اولین شخص (person1) جوانترین شخص است. سپس با استفاده از پیاده سازی متد CompareTo() تست می کنیم که آیا شخص دوم (person2) از شخص اول جوانتر است یا نه. در داخل متد مذکور سن شخص دوم و اول را با هم مقایسه می کنیم. اگر سن شخص دوم کمتر بود، باید مقدار 1- برگشت داده شده و در خط 24، person2 به عنوان جوانترین شخص معرفی شود. در خطوط 27-26 از تکنیکی مشابه برای شخص سوم استفاده کرده ایم. بعد از مقایسه جوانترین شخص در خط 29 به عنوان نتیجه برگشت داده می شود. در خط 10 متد GetOldest() که در خطوط 43-32 تعریف شده است فراخوانی می شود. کد های داخل این مت شبیه به متد GetYoungest() است با این تفاوت که تست می شود که آیا سن شخص دیگر بزرگتر از سن شخص مورد نظر ماست یا نه؟ بنابراین باید انتظار داشته باشیم که مقدار 1 به جای 1- توسط متد برگشت داده شود. در خطوط 15-12 نام جوانترین و پیرترین شخص چاپ می شود.

 

رابط <IComparer<T

IComparer<T> در یک کلاس جداگانه پیاده سازی می شود. همانطور که از نام این رابط پیداست، پیاده سازی آن باعث ایجاد یک کلاس مقایسه پذیر می شود. به وسیله این رابط  می توان چندین مقایسه برای کلاس Person ایجاد کرد. مثلا در مثال کلاس Person، اشیاء ایجاد شده  بر اساس سن(age)، نام و یا نام خانوادگی مورد مقایسه قرار می گیرند.

  1: public class FirstNameComparer : IComparer
  2: {
  3:     public int Compare(Person person1, Person person2)
  4:     {
  5:         return person1.FirstName.CompareTo(person2.FirstName);
  6:     }
  7: }
  8:  
  9: public class LastNameComparer : IComparer
 10: {
 11:     public int Compare(Person person1, Person person2)
 12:     {
 13:         return person1.LastName.CompareTo(person2.LastName);
 14:     }
 15: }
 16:  
 17: public class AgeComparer : IComparer
 18: {
 19:     public int Compare(Person person1, Person person2)
 20:     {
 21:         return person1.CompareTo(person2);
 22:     }
 23: }

هنگام استفاده از این رابط لازم است یک متد به نام ()Compare که دو شیء قبول می کند و یک عدد صحیح را به عنوان نتیجه بر می گرداند را پیاده سازی کند. از آنجاییکه از رابط IComparer<Person>  استفاده کرده ایم متد ()Compare به طور خودکار دو شئ Person قبول می کند. در مثال بالا با استفاده از کلاس FirstNameComparer  که رابط IComparer  را پیاده سازی می کند، دو شئ Person بر اساس نام مقایسه می شوند. در متد ()Compare به سادگی و با استفاده از متد از پیش تعریف شده ()CompareTo از کلاس String استفاده کرده ایم (چون خاصیت FirstName  یک رشته است) و یک مقدار را به عنوان نتیجه بر می گردانیم.

به روشی مشابه از کلاس های LastNameComparer و AgeComparer برای مقایسه اشیاء بر اساس نام خانوادگی و سن استفاده می کنیم. متد ()Compare در صورتی که دو پارامتر با هم برابر باشند، مقدار 0 ، اگر پارامتر اول از پارامتر دوم بزرگتر باشد، مقداری بزرگتر از 0 و اگر پارامتر اول از پارامتر دوم کوچکتر باشد مقداری کوچکتر از 0 را بر می گرداند. در مثال زیر از کاربر سوال می شود که لیستی از اشیاء را قرار است بر اساس کدام خاصیت مرتب کند :

  1: public class Program
  2: {
  3:     static void Main(string[] args)
  4:     {
  5:         List persons = new List { 
  6:                 new Person { FirstName = "John", LastName = "Smith", Age = 21 },
  7:                 new Person { FirstName = "Mark", LastName = "Logan", Age = 19 },
  8:                 new Person { FirstName = "Luke", LastName = "Adams", Age = 20 }};
  9:  
 10:         Console.WriteLine("Original Order");
 11:         foreach(Person p in persons)
 12:             Console.WriteLine("{0} {1}, Age: {2}", p.FirstName, p.LastName, p.Age);
 13:  
 14:         Console.WriteLine("\nSort persons based on their:");
 15:         Console.WriteLine("[1] FirstName\n[2] LastName\n[3]Age");
 16:  
 17:         Console.Write("Enter your choice: ");
 18:         int choice = Int32.Parse(Console.ReadLine());
 19:  
 20:         ReorderPersons(choice, persons);
 21:  
 22:         Console.WriteLine("New Order");
 23:         foreach (Person p in persons)
 24:             Console.WriteLine("{0} {1}, Age: {2}", p.FirstName, p.LastName, p.Age);
 25:     }
 26:  
 27:     private static void ReorderPersons(int choice, List persons)
 28:     {
 29:         IComparer comparer;
 30:  
 31:         if (choice == 1)
 32:             comparer = new FirstNameComparer();
 33:         else if (choice == 2)
 34:             comparer = new LastNameComparer();
 35:         else
 36:             comparer = new AgeComparer();
 37:  
 38:         persons.Sort(comparer);
 39:     }
 40: }
Original Order
John Smith, Age: 21
Mark Logan, Age: 19
Luke Adams, Age: 20

Sort persons based on their:
[1] FirstName
[2] LastName
[3]Age
Enter your choice: 1
New Order
John Smith, Age: 21
Luke Adams, Age: 20
Mark Logan, Age: 19
Original Order
John Smith, Age: 21
Mark Logan, Age: 19
Luke Adams, Age: 20

Sort persons based on their:
[1] FirstName
[2] LastName
[3]Age
Enter your choice: 2
New Order
Luke Adams, Age: 20
Mark Logan, Age: 19
John Smith, Age: 21
Original Order
John Smith, Age: 21
Mark Logan, Age: 19
Luke Adams, Age: 20

Sort persons based on their:
[1] FirstName
[2] LastName
[3]Age
Enter your choice: 3
New Order
Mark Logan, Age: 19
Luke Adams, Age: 20
John Smith, Age: 21

در خطوط 8-5 اشیا با مقادیر از پیش تعریف شده ای برای هر یک از خاصیت هایشان ایجاد شده است.در خطوط 12-11 ترتیب عادی و اصلی این اشیاء نمایش داده شده است. در خطوط 15-14 لیستی از انتخاب هایی که کاربر بر اساس آنها می تواند عملیات مرتب سازی را انجام دهد آورده شده است.در خطوط 18-17 از کاربر در مورد انتخابش سوال می شود. در خط 20 متد ()ReorderPersons متد از پیش تعریف شده ی خطوط 39-27 را فراخوانی می کنیم. این متد انتخاب کاربر و لیستی از اشیا که قرار است بر اساس خاصیتی که کاربر انتخاب کرده است مرتب شوند را قبول می کند. در داخل متد یک متغیر تعریف کرده ایم که از نوع IComparer<Person> است و در نتیجه می تواند هر نوع کلاسی که رابط مذکور را پیاده سازی می کند را شامل شود. در خطوط 36-31 چک می کنیم که اگر کاربر یک مقدار عددی خاص را انتخاب کرد، چه کارهایی انجام شود. در خط 38 از متد ()Sort کلاس List<T>  استفاده کرده ایم. این متد دارای یک نسخه سربارگذاری شده است که یک شی IComparer<T> را قبول می کند. ما در خط 29 کلاس مقایسه کننده بر اساس نوع انتخاب کاربر را به این متد می دهیم و سپس متد ()Sort شی Person را بر اساس این کلاس مرتب می کند.

لطفا اگر نظر، پیشنهاد و یا انتقادی در باره مطلب بالا دارید در قسمت زیر و اگر سوالی دارید در بخش پرسش و پاسخ مطرح بفرمایید.