المساواة والتجزئة (القيمة مقابل المرجع)
افهم مساواة المراجع مقابل مساواة القيم، وتجاوز Equals/GetHashCode بصورة صحيحة، وطبّق IEquatable ، واستخدم مقارنات مخصصة مع المجموعات والقواميس.
المساواة والتجزئة (القيمة مقابل المرجع) درس مجاني في C# Academy على CoddyKit. هذا هو الدرس 3 من أصل 3. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في C# Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة C# Academy 3 دروس في المجموع.
أساسيات المساواة
الهدف: جعل المساواة تعمل كما تتوقع.
- مساواة المراجع مقابل مساواة القيم
- عقد Equals + GetHashCode
- IEquatable<T> لتحسين السرعة
- مقارنات مخصصة عندما يتعذر تعديل النوع
مشكلة مساواة المراجع
تستخدم الفئات افتراضيًا مساواة المراجع. لا يتساوى كائنان منفصلان يحتويان على البيانات نفسها ما لم تُنفّذ مساواة القيم.
using System;
using System.Collections.Generic;
public sealed class Point // no overrides
{
public int X;
public int Y;
public Point(int x, int y){ X = x; Y = y; }
}
public class Program
{
public static void Main(string[] args)
{
Point a = new Point(1, 2);
Point b = new Point(1, 2);
Console.WriteLine("a == b ? " + (a == b)); // reference equality: False
Console.WriteLine("a.Equals(b) ? " + a.Equals(b)); // False
HashSet<Point> set = new HashSet<Point>();
set.Add(a);
Console.WriteLine("Contains b? " + set.Contains(b)); // False (unexpected)
}
}
تنفيذ مساواة القيم
نفّذ IEquatable<T>، وتجاوز Equals وGetHashCode. يجب أن يتطابق رمز التجزئة مع المساواة: الكائنات المتساوية ← رمز التجزئة نفسه.
using System;
using System.Collections.Generic;
public sealed class ValuePoint : IEquatable<ValuePoint>
{
public int X;
public int Y;
public ValuePoint(int x, int y){ X = x; Y = y; }
public bool Equals(ValuePoint other)
{
if (ReferenceEquals(other, null)) return false;
if (ReferenceEquals(this, other)) return true;
return X == other.X && Y == other.Y;
}
public override bool Equals(object obj)
{
return Equals(obj as ValuePoint);
}
public override int GetHashCode()
{
// Simple, stable combination (avoid randomness)
unchecked
{
int hash = 17;
hash = hash * 31 + X.GetHashCode();
hash = hash * 31 + Y.GetHashCode();
return hash;
}
}
}
public class Program
{
public static void Main(string[] args)
{
ValuePoint a = new ValuePoint(1, 2);
ValuePoint b = new ValuePoint(1, 2);
Console.WriteLine("a.Equals(b)? " + a.Equals(b)); // True
HashSet<ValuePoint> set = new HashSet<ValuePoint>();
set.Add(a);
Console.WriteLine("Contains b? " + set.Contains(b)); // True (value semantics)
}
}
مقارن مخصص
مرّر مقارن IEqualityComparer<T> مخصصًا إلى HashSet/Dictionary عندما يتعذر تغيير النوع نفسه.
using System;
using System.Collections.Generic;
public sealed class Person // imagine from a library; cannot edit
{
public string Name;
public int BirthYear;
public Person(string name, int year){ Name = name; BirthYear = year; }
}
public sealed class PersonComparer : IEqualityComparer<Person>
{
public bool Equals(Person a, Person b)
{
if (ReferenceEquals(a, b)) return true;
if (ReferenceEquals(a, null) || ReferenceEquals(b, null)) return false;
return a.Name == b.Name && a.BirthYear == b.BirthYear;
}
public int GetHashCode(Person p)
{
if (ReferenceEquals(p, null)) return 0;
unchecked
{
int h = 23;
h = h * 31 + (p.Name == null ? 0 : p.Name.GetHashCode());
h = h * 31 + p.BirthYear.GetHashCode();
return h;
}
}
}
public class Program
{
public static void Main(string[] args)
{
Person p1 = new Person("Ada", 1815);
Person p2 = new Person("Ada", 1815);
HashSet<Person> set = new HashSet<Person>(new PersonComparer());
set.Add(p1);
Console.WriteLine("Contains p2? " + set.Contains(p2)); // True via comparer
}
}
العقد والمزالق
القواعد:
- إذا كانت نتيجة Equals(a,b) هي true ⇒ يجب أن يكون GetHashCode(a) == GetHashCode(b)
- ينبغي أن تكون المساواة انعكاسية، متناظرة، ومتعدية
- استخدم الحقول غير القابلة للتغيير للتجزئة متى أمكن
- لا تستخدم قيمًا عشوائية في GetHashCode
ملاحظة حول البنية والفئة
تقارن البُنى بحسب الحقول افتراضيًا (دلالات القيم). أما الفئات فتُقارن بحسب المرجع ما لم تُنفّذ مساواة القيم.
using System;
public struct PointS // struct: value type
{
public int X;
public int Y;
public PointS(int x, int y){ X = x; Y = y; }
}
public class Program
{
public static void Main(string[] args)
{
PointS a = new PointS(1, 2);
PointS b = new PointS(1, 2);
Console.WriteLine("Struct equality: " + a.Equals(b)); // True by default (field-wise)
}
}
عقد المساواة للمجموعات
مراجعة
مراجعة: تحتاج الفئات إلى Equals/GetHashCode (وغالبًا IEquatable<T>) لتوفير دلالات القيم؛ استخدم المقارنات المخصصة عندما يتعذر تعديل الأنواع.
الأسئلة الشائعة
هل درس «المساواة والتجزئة (القيمة مقابل المرجع)» مجاني؟
نعم — نص درس «المساواة والتجزئة (القيمة مقابل المرجع)» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة C# Academy، انتقل إلى CoddyKit PRO. تتضمن دورة C# Academy 3 دروس في المجموع.
ماذا ستتعلم في «المساواة والتجزئة (القيمة مقابل المرجع)»؟
افهم مساواة المراجع مقابل مساواة القيم، وتجاوز Equals/GetHashCode بصورة صحيحة، وطبّق IEquatable ، واستخدم مقارنات مخصصة مع المجموعات والقواميس. تتمرن على C# Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ C# Academy؟
لا تُشترط خبرة سابقة. C# Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 3.
كم من الوقت يستغرق درس «المساواة والتجزئة (القيمة مقابل المرجع)»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس C# Academy هذا؟
نعم. كل درس في C# Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- HashSet وSortedSet وQueue وStack
- ConcurrentDictionary والمجموعات غير القابلة للتغيير
- المساواة والتجزئة (القيمة مقابل المرجع)