تمرین، شماری از ویژگیهای برابری در C# را نشان میدهد:
Object.Equals()== و != Test میشود. این کار در مقایسه با استفاده از متد Equals() که برای این انواع هم در دسترس است، مرسومتر به شمار میرود. برنامهنویسان جاوا هنگام کار با رشتهها باید به این نکته توجه داشته باشند که == در C# بر اساس مقدار مقایسه میکند، اما در جاوا، وقتی به زبان پیشین خود بازمیگردند، بر اساس ارجاع.Equals() که از object به ارث رسیده است مقایسه میشوند. اگر هدف شما از Test برابری این باشد که مطمئن شوید دو شیء دقیقاً همان نمونهی یکساناند، تکیه بر پیادهسازی object کافی است. در غیر این صورت باید object.Equals() را بازنویسی کنید.Equals() را بازنویسی کنید.Equals() بازنویسیشده شامل Testهای برابری روی اعضای انواع ساده با == و انواع ارجاعی با فراخوانیهای بازگشتی Equals() خواهد بود.class StatusBar
{
private readonly int width = 200, height = 20;
public override bool Equals(object other)
{
// ... null and type checks and performance optimisations
return width == (other as StatusBar).width && height == (other as StatusBar).height;
}
}
class Window
{
private readonly string title = "Main";
private readonly StatusBar statusBar = new StatusBar();
public override bool Equals(object other)
{
// ... null and type checks and performance optimisations
return title == (other as Window).title && statusBar.Equals((other as Window).statusBar);
}
}
object.ReferenceEquals() برای مقایسهی دو شیء به کار میرود تا مشخص شود آیا آنها یک نمونهی یکساناند یا نه. این کار خوانایی را بالا میبرد و در جایی که Equals() و == بازنویسی یا سربارگذاری شدهاند، ضروری است.var winA = new Window(); // above code shows that all windows are equal
var winB = new Window();
ReferenceEquals(winA, winB);
// => false
var winC = winA;
ReferenceEquals(winA, winC);
// => true
public override bool Equals(object obj)، محیطهای توسعهی یکپارچه معمولاً سربارگذاری protected bool Equals(FacialFeatures other) را برای زمانی که وراثت در کار است تولید میکنند. یک کلاس مشتقشده میتواند Equals() کلاس پایه را فراخوانی کند و سپس Test خودش را اضافه کند.== استفاده نکنید مگر اینکه عملگر == و همچنین متد Equals() را در کلاس خود سربارگذاری کرده باشید (به تمرین operator-overloading نگاه کنید)، یا اینکه فقط برابری ارجاعها برایتان مهم باشد.structs در تمرین structs بررسی میشوند.tuples در تمرین tuples بررسی میشود.protected bool Equals(T other)
{
return field1 == other.field1 && field2.Equals(other.field2);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((T) obj);
}
SequenceEquals() را فراهم میکند، اما در نبود LINQ باید هر دو مجموعه را پیمایش کرد و عناصر را یکییکی با هم مقایسه کرد.== و != با کلاسهای خودتان، به تمرین operator-overloading نگاه کنید.Object.GetHashCode()object.GetHashCode() یک کد هش در قالب یک عدد صحیح ۳۲بیتی برمیگرداند. کد هش را کلاسهای دیکشنری و مجموعه مانند Dictionary<T> و HashSet<T> برای ذخیره و بازیابی اشیا به شکلی کارا استفاده میکنند. در مورد دیکشنریها، هش کردن به کلیدها مربوط میشود.Equals() را بازنویسی میکنید، GetHashCode() را هم بازنویسی کنید. بین Equals() و GetHashCode() رابطهای هست که برای رفتار درست کلاسهای دیکشنری و مجموعهی هش و هر کلاس دیگری که از کد هش استفاده میکند باید برقرار بماند. انتظار میرود متد را طوری پیادهسازی کنید که برای نگهدارندگانی که ممکن است بعداً مجموعهای مبتنی بر کد هش اضافه کنند، تلهای گذاشته نشود.Equal() مقدار «درست» را برمیگرداند)، آنگاه GetHashCode() آن دو شیء باید مقدار یکسانی برگرداند. این رابطه در جهت معکوس برقرار نیست. متقارن نیست. یک تابع جستوجو را در نظر بگیرید که ابتدا بر اساس کد هش به یک «سبد» میرود و سپس عنصر خاص را با Test برابری بیرون میکشد.HashCode.Combine() را فراخوانی کنید و مقادیری را که در Test برابری استفاده شدهاند (یا زیرمجموعهای از آنها) به آن بدهید. به یاد داشته باشید که هرچه اطلاعات بیشتری به Combine() بدهید، پیادهسازی هش احتمالاً کاراتر خواهد بود.public class Assessment
{
private int rating;
private Person boss;
public override int GetHashCode()
{
return HashCode.Combine(rating, boss);
}
}
برای بهبود جزئی کارایی، بهویژه وقتی اشیا به مجموعهها تعلق دارند، میتوانید یک عضو سربارگذاریشدهی public bool Equals(T other) اضافه کنید.
این کار مقداری بررسی نوع برای انواع ارجاعی را حذف میکند و برای انواع مقداری یک مرحله جعبهسازی را صرفهجویی میکند، چون دیگر نیازی نیست بهعنوان آرگومان public override bool Equals(object other) به یک شیء تبدیل شوند (جعبهسازی شوند).
اگر رابط IEquatable<T> را به کلاس خود اضافه کنید، باید این سربارگذاری را پیادهسازی کنید. مگر اینکه Code شما روتینهایی داشته باشد که اشیایی از نوع IEquatable<T> میگیرند (و احتمالاً کار جالبی مربوط به برابری، مستقل از کلاس پیادهساز، انجام میدهند)، واقعاً دلیل قانعکنندهی دیگری برای گنجاندن این رابط وجود ندارد.
IEqualityComparer<T>اگر کلاسی دارید که میتوان آن را به دو روش متفاوت بهطور یکتا شناسایی کرد، مثلاً کلاس Person که یک SSID و یک نشانی ایمیل یکتا دارد، آنگاه .NET ابزاری فراهم میکند تا دو مجموعهی متفاوت بتوانند از Testهای کد هش و برابری متفاوتی استفاده کنند. هر کدام میتوانند پیادهسازی متفاوتی از IEqualityComparer<T> با متدهای Equals() و GetHashCode() خود داشته باشند. میتوانید یک دیکشنری با کلید SSID و دیگری با کلید نشانی ایمیل داشته باشید.
جایی که IEqualityComparer<T> در کار است، معمولاً همچنان Equals() و GetHashCode() را روی کلاس عنصر خود بازنویسی میکنید تا از بروز مشکل بیرون از کلاسهای مجموعه جلوگیری کنید.
هنگام استفاده از IEqualityComparer<T> یک نکته این است که متدهای خصوصی و مانند آن برای Test برابری در دسترس نخواهند بود.
اگر فقط یک مجموعهی هششده در کار باشد، ممکن است بهتر باشد از IEqualityComparer<T> پرهیز کنید و برابری و کد هش را در خود شیء کپسوله کنید. در وهلهی اول ایدهآل نیست که وابستگیای کلیدی مانند وابستگی بین شیء و مجموعهی هششده را نتوان با کامپایلر الزام کرد، اما اگر منطق هش و مقایسه جای دیگری باشد، برای نگهدارنده حتی آسانتر میشود که بدون توجه به پیامدهایش برای مجموعه، اعضای یک کلاس را تغییر دهد.
یکی از انواع اولیهای که میتواند برنامهنویس غافل را به چالش بکشد، Test کردنِ برابری مقادیر اعشاری است. این موضوع در سند about.md مفهوم floating-point-numbers بررسی میشود.
این مقاله برخی از تصمیمهایی را نشان میدهد که باید دربارهی برابری و وراثت گرفته شوند. اینها تنها زمانی به کار شما میآیند که نمونههایی از یک کلاس و یک کلاس مشتقشده را دستکاری میکنید و لازم است برابری بین آن دو را Test کنید.