دو رابط مفید برای مقایسه اشیایی که توسط کاربر تعریف شده اند وجود دارد. این دو رابط IComparable<T> و IComparer<T> می باشند.
رابط <IComparable<T
رابط IComparable<T> در یک کلاس پیاده سازی می شود و اجازه می دهد کلاس یا شی ایجاد شده از کلاس با اشیاء دیگر از همین کلاس مقایسه شوند. به این نکته توجه کنید که نسخه های غیر جنریک این دو رابط نیز وجود دارد ولی کار کردن با نسخه های جنریک آنها بسیار راحت تر بوده و شما نیاز به تبدیل برای مقایسه اشیاء ندارید.
حال به نحوه استفاده از رابط IComparable<T> می پردازیم. در مثال زیر یک کلاس نشان داده شده است که رابط IComparable<T> را پیاده سازی می کند:
public class Person : IComparable<Person>
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public int CompareTo(Person other)
{
if (this.Age > other.Age)
return 1;
else if (this.Age < other.Age)
return -1;
else
return 0;
}
}
وقتی که یک کلاس از رابط IComparable<T> استفاده می کند، لازم است که تنها متد آن یعنی متد CompareTo() را نیز پیاده سازی کند. متد CompareTo() یک مقدار صحیح را بر می گرداند. این متد همچنین یک آرگومان قبول می کند که همان شیی است که قرار است با شی جاری مقایسه شود. در داخل متد CompareTo() در مثال بالا ما سن (age) شخص فعلی را با سن شخص دیگر مقایسه کرده ایم. طبق قرارداد اگر سن شخص مورد نظر ما از سن شخص دیگر بیشتر بود مقداری بزرگتر از صفر، اگر کمتر بود مقداری کمتر از صفر و اگر مساوی بود مقدار صفر برگشت داده می شود. کد زیر پیاده سازی رابط IComparable<T> توسط شی Person را نشان می دهد. برنامه جوانترین و پیرترین شخص را تشخیص می دهد.
public class Program
{
static void Main(string[] args)
{
Person person1 = new Person { FirstName = "John", LastName = "Smith", Age = 21 };
Person person2 = new Person { FirstName = "Mark", LastName = "Logan", Age = 19 };
Person person3 = new Person { FirstName = "Luke", LastName = "Adams", Age = 20 };
Person youngest = GetYoungest(person1, person2, person3);
Person oldest = GetOldest(person1, person2, person3);
Console.WriteLine("The youngest person is {0} {1}.",
youngest.FirstName, youngest.LastName);
Console.WriteLine("The oldest person is {0} {1}.",
oldest.FirstName, oldest.LastName);
Console.ReadKey();
}
private static Person GetYoungest(Person person1, Person person2, Person person3)
{
Person youngest = person1;
if (person2.CompareTo(youngest) == -1)
youngest = person2;
if (person3.CompareTo(youngest) == -1)
youngest = person3;
return youngest;
}
private static Person GetOldest(Person person1, Person person2, Person person3)
{
Person oldest = person1;
if (person2.CompareTo(oldest) == 1)
oldest = person2;
if (person3.CompareTo(oldest) == 1)
oldest = person3;
return oldest;
}
}
در خطوط 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)، نام و یا نام خانوادگی مورد مقایسه قرار می گیرند.
{
public int Compare(Person person1, Person person2)
{
return person1.FirstName.CompareTo(person2.FirstName);
}
}
public class LastNameComparer : IComparer
{
public int Compare(Person person1, Person person2)
{
return person1.LastName.CompareTo(person2.LastName);
}
}
public class AgeComparer : IComparer
{
public int Compare(Person person1, Person person2)
{
return person1.CompareTo(person2);
}
}
هنگام استفاده از این رابط لازم است یک متد به نام ()Compare که دو شیء قبول می کند و یک عدد صحیح را به عنوان نتیجه بر می گرداند را پیاده سازی کند. از آنجاییکه از رابط IComparer<Person> استفاده کرده ایم متد ()Compare به طور خودکار دو شئ Person قبول می کند. در مثال بالا با استفاده از کلاس FirstNameComparer که رابط IComparer را پیاده سازی می کند، دو شئ Person بر اساس نام مقایسه می شوند. در متد ()Compare به سادگی و با استفاده از متد از پیش تعریف شده ()CompareTo از کلاس String استفاده کرده ایم (چون خاصیت FirstName یک رشته است) و یک مقدار را به عنوان نتیجه بر می گردانیم.
به روشی مشابه از کلاس های LastNameComparer و AgeComparer برای مقایسه اشیاء برا اساس نا خانوادگی و سن استفاده می کنیم. متد Compare در صورتی که دو پارامتر با هم برابر باشند مقدار 0 ، اگر مپارامتر اول از پارامتر دوم بزگتر باشد مقداری بزرگتر از 0 و اگر پارمتر اول از پارامتر دوم کوچکتر باشد مقداری کوچکتر از 0 را بر می گرداند. در مثال زیر از کاربر سوال می شود که لیستی از اشیاء را قرار است بر اساس کدام خاصیت مرتب کند :
public class Program
{
static void Main(string[] args)
{
List persons = new List {
new Person { FirstName = "John", LastName = "Smith", Age = 21 },
new Person { FirstName = "Mark", LastName = "Logan", Age = 19 },
new Person { FirstName = "Luke", LastName = "Adams", Age = 20 }};
Console.WriteLine("Original Order");
foreach(Person p in persons)
Console.WriteLine("{0} {1}, Age: {2}", p.FirstName, p.LastName, p.Age);
Console.WriteLine("\nSort persons based on their:");
Console.WriteLine("[1] FirstName\n[2] LastName\n[3]Age");
Console.Write("Enter your choice: ");
int choice = Int32.Parse(Console.ReadLine());
ReorderPersons(choice, persons);
Console.WriteLine("New Order");
foreach (Person p in persons)
Console.WriteLine("{0} {1}, Age: {2}", p.FirstName, p.LastName, p.Age);
}
private static void ReorderPersons(int choice, List persons)
{
IComparer comparer;
if (choice == 1)
comparer = new FirstNameComparer();
else if (choice == 2)
comparer = new LastNameComparer();
else
comparer = new AgeComparer();
persons.Sort(comparer);
}
}
در خطوط 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 را بر اساس این کلاس مرتب می کند.
لینک مرجع : w3-farsi.com