Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Kayıtlar otomatik olarak değer eşitliğini uygular. Türünüz veri modellerken ve değer eşitliği uygulaması gerektiğinde bir class
yerine bir record
tanımlamayı düşünün.
Bir sınıf veya yapı tanımladığınızda, tür için değer eşitliği (veya denkliği) için özel bir tanım oluşturmanın mantıklı olup olmadığına karar verirsiniz. Genellikle, bir koleksiyona türdeki nesneleri eklemeyi beklediğinizde veya birincil amacı bir alan veya özellik kümesi depolamak olduğunda değer eşitliği uygularsınız. Değer eşitliği tanımınızı türdeki tüm alanların ve özelliklerin karşılaştırmasını temel alabilir veya tanımı bir alt kümeye dayandırabilirsiniz.
Her iki durumda da ve hem sınıflarda hem de yapılarda uygulamanız beş denklik garantisine uymalıdır (aşağıdaki kurallar için , x
ve y
değerlerinin z
null olmadığını varsayın):
Esnek özelliği:
x.Equals(x)
döndürürtrue
.Simetrik özellik:
x.Equals(y)
ile aynı değeriy.Equals(x)
döndürür.Geçişli özellik: Eğer
(x.Equals(y) && y.Equals(z))
true
döndürürse, o zamanx.Equals(z)
true
döndürür.x ve y tarafından başvuruda bulunan nesneler değiştirilmediği sürece birbirini izleyen çağrılar
x.Equals(y)
aynı değeri döndürür.Null olmayan değerler null değerine eşit değildir. Ancak null
x.Equals(y)
olduğundax
bir özel durum oluşturur. Bu,Equals
bağımsız değişkenine bağlı olarak 1 veya 2. kuralı bozar.
Tanımladığınız herhangi bir yapı, Object.Equals(Object) yönteminin System.ValueType geçersiz kılınmasından devraldığı varsayılan değer eşitliği uygulamasına zaten sahiptir. Bu uygulama, türdeki tüm alanları ve özellikleri incelemek için yansıma kullanır. Bu uygulama doğru sonuçlar üretse de, türü için özel olarak yazdığınız özel bir uygulamayla karşılaştırıldığında nispeten yavaştır.
Değer eşitliği için uygulama ayrıntıları sınıflar ve yapılar için farklıdır. Ancak, hem sınıflar hem de yapılar eşitlik uygulamak için aynı temel adımları gerektirir:
SanalObject.Equals(Object) yöntemi geçersiz kılın. Çoğu durumda,
bool Equals( object obj )
uygulamanız, System.IEquatable<T> arabiriminin bir uygulaması olan tür özelEquals
yöntemini çağırmalıdır. (Bkz. 2. adım.)Türe System.IEquatable<T> özgü
Equals
bir yöntem sağlayarak arabirimini uygulayın. Burada gerçek denklik karşılaştırması gerçekleştirilir. Örneğin, türünizdeki yalnızca bir veya iki alanı karşılaştırarak eşitlik tanımlamaya karar vekleyebilirsiniz.Equals
'den özel durum fırlatmayın. Kalıtım yoluyla birbirine bağlı sınıflar için:Bu yöntem yalnızca sınıfında bildirilen alanları incelemelidir. Temel sınıftaki alanları incelemek için
base.Equals
çağrı yapmalıdır. (Doğrudan Object öğesinden tür devralıyorsabase.Equals
çağırmayın, çünkü Object'nin Object.Equals(Object) üzerindeki uygulaması bir başvuru eşitliği denetimi yapar.)yalnızca karşılaştırılan değişkenlerin çalışma zamanı türleri aynıysa iki değişken eşit kabul edilmelidir. Ayrıca, bir değişkenin çalışma zamanı ve derleme zamanı türleri farklıysa,
IEquatable
yöntemi için çalışma zamanı türünün uygulanmasınınEquals
kullanıldığından emin olun. Çalışma zamanı türlerinin her zaman doğru karşılaştırıldığından emin olmak için bir strateji yalnızca sınıflardaIEquatable
uygulamaktırsealed
. Daha fazla bilgi için bu makalenin devamında yer alan sınıf örneğine bakın.
İsteğe bağlı ama önerilir: == ve != işleçlerini aşırı yükleyin.
Object.GetHashCode'yi, değer açısından eşit olan iki nesnenin aynı karma kodu üretmesini sağlamak için geçersiz kılın.
İsteğe bağlı: "büyüktür" veya "küçüktür" tanımlarını desteklemek için, türünüz için IComparable<T> arabirimini uygulayın ve ayrıca <= ve >= işleçlerini aşırı yükleyin.
Not
Gereksiz bir ortak kod olmadan değer eşitliği semantiği elde etmek için kayıtları kullanabilirsiniz.
Sınıf örneği
Aşağıdaki örnekte, bir sınıfta değer eşitliğinin nasıl uygulandığı gösterilmektedir (başvuru türü).
namespace ValueEqualityClass;
class TwoDPoint : IEquatable<TwoDPoint>
{
public int X { get; private set; }
public int Y { get; private set; }
public TwoDPoint(int x, int y)
{
if (x is (< 1 or > 2000) || y is (< 1 or > 2000))
{
throw new ArgumentException("Point must be in range 1 - 2000");
}
this.X = x;
this.Y = y;
}
public override bool Equals(object obj) => this.Equals(obj as TwoDPoint);
public bool Equals(TwoDPoint p)
{
if (p is null)
{
return false;
}
// Optimization for a common success case.
if (Object.ReferenceEquals(this, p))
{
return true;
}
// If run-time types are not exactly the same, return false.
if (this.GetType() != p.GetType())
{
return false;
}
// Return true if the fields match.
// Note that the base class is not invoked because it is
// System.Object, which defines Equals as reference equality.
return (X == p.X) && (Y == p.Y);
}
public override int GetHashCode() => (X, Y).GetHashCode();
public static bool operator ==(TwoDPoint lhs, TwoDPoint rhs)
{
if (lhs is null)
{
if (rhs is null)
{
return true;
}
// Only the left side is null.
return false;
}
// Equals handles case of null on right side.
return lhs.Equals(rhs);
}
public static bool operator !=(TwoDPoint lhs, TwoDPoint rhs) => !(lhs == rhs);
}
// For the sake of simplicity, assume a ThreeDPoint IS a TwoDPoint.
class ThreeDPoint : TwoDPoint, IEquatable<ThreeDPoint>
{
public int Z { get; private set; }
public ThreeDPoint(int x, int y, int z)
: base(x, y)
{
if ((z < 1) || (z > 2000))
{
throw new ArgumentException("Point must be in range 1 - 2000");
}
this.Z = z;
}
public override bool Equals(object obj) => this.Equals(obj as ThreeDPoint);
public bool Equals(ThreeDPoint p)
{
if (p is null)
{
return false;
}
// Optimization for a common success case.
if (Object.ReferenceEquals(this, p))
{
return true;
}
// Check properties that this class declares.
if (Z == p.Z)
{
// Let base class check its own fields
// and do the run-time type comparison.
return base.Equals((TwoDPoint)p);
}
else
{
return false;
}
}
public override int GetHashCode() => (X, Y, Z).GetHashCode();
public static bool operator ==(ThreeDPoint lhs, ThreeDPoint rhs)
{
if (lhs is null)
{
if (rhs is null)
{
// null == null = true.
return true;
}
// Only the left side is null.
return false;
}
// Equals handles the case of null on right side.
return lhs.Equals(rhs);
}
public static bool operator !=(ThreeDPoint lhs, ThreeDPoint rhs) => !(lhs == rhs);
}
class Program
{
static void Main(string[] args)
{
ThreeDPoint pointA = new ThreeDPoint(3, 4, 5);
ThreeDPoint pointB = new ThreeDPoint(3, 4, 5);
ThreeDPoint pointC = null;
int i = 5;
Console.WriteLine($"pointA.Equals(pointB) = {pointA.Equals(pointB)}");
Console.WriteLine($"pointA == pointB = {pointA == pointB}");
Console.WriteLine($"null comparison = {pointA.Equals(pointC)}");
Console.WriteLine($"Compare to some other type = {pointA.Equals(i)}");
TwoDPoint pointD = null;
TwoDPoint pointE = null;
Console.WriteLine($"Two null TwoDPoints are equal: {pointD == pointE}");
pointE = new TwoDPoint(3, 4);
Console.WriteLine($"(pointE == pointA) = {pointE == pointA}");
Console.WriteLine($"(pointA == pointE) = {pointA == pointE}");
Console.WriteLine($"(pointA != pointE) = {pointA != pointE}");
System.Collections.ArrayList list = new System.Collections.ArrayList();
list.Add(new ThreeDPoint(3, 4, 5));
Console.WriteLine($"pointE.Equals(list[0]): {pointE.Equals(list[0])}");
// Keep the console window open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/* Output:
pointA.Equals(pointB) = True
pointA == pointB = True
null comparison = False
Compare to some other type = False
Two null TwoDPoints are equal: True
(pointE == pointA) = False
(pointA == pointE) = False
(pointA != pointE) = True
pointE.Equals(list[0]): False
*/
Sınıflarda (başvuru türleri), her iki Object.Equals(Object) yöntemin de varsayılan uygulaması, değer eşitliği kontrolü değil, başvuru eşitliği karşılaştırması gerçekleştirir. Bir uygulayıcı sanal yöntemi geçersiz kıldığında, amaç değer eşitliği semantiği vermektir.
==
ve !=
işleçleri, sınıfı aşırı yüklemese bile sınıflarla birlikte kullanılabilir. Ancak, varsayılan davranış bir başvuru eşitliği denetimi gerçekleştirmektir. Bir sınıfta, eğer Equals
yöntemini aşırı yüklerseniz, ==
ve !=
işleçlerini de aşırı yüklemelisiniz, ancak bu zorunlu değildir.
Önemli
Yukarıdaki örnek kod, her devralma senaryolarını beklediğiniz gibi işlemeyebilir. Aşağıdaki kodu inceleyin:
TwoDPoint p1 = new ThreeDPoint(1, 2, 3);
TwoDPoint p2 = new ThreeDPoint(1, 2, 4);
Console.WriteLine(p1.Equals(p2)); // output: True
Bu kod, farklı z
değerlerine rağmen p1
'in p2
'e eşit olduğunu bildirir. Derleyici, derleme zamanı türüne göre TwoDPoint
'in uygulamasını seçtiği için IEquatable
farkı yoksayılır.
Türlerin record
yerleşik değer eşitliği, bunun gibi senaryoları doğru işler. Eğer TwoDPoint
ve ThreeDPoint
, record
türleri olsaydı, p1.Equals(p2)
'nin sonucu False
olurdu. Daha fazla bilgi için bkz. Tür devralma hiyerarşilerinde record
eşitlik.
Yapı örneği
Aşağıdaki örnekte, bir yapıda değer eşitliğinin (değer türü) nasıl uygulanacakları gösterilmektedir:
namespace ValueEqualityStruct
{
struct TwoDPoint : IEquatable<TwoDPoint>
{
public int X { get; private set; }
public int Y { get; private set; }
public TwoDPoint(int x, int y)
: this()
{
if (x is (< 1 or > 2000) || y is (< 1 or > 2000))
{
throw new ArgumentException("Point must be in range 1 - 2000");
}
X = x;
Y = y;
}
public override bool Equals(object? obj) => obj is TwoDPoint other && this.Equals(other);
public bool Equals(TwoDPoint p) => X == p.X && Y == p.Y;
public override int GetHashCode() => (X, Y).GetHashCode();
public static bool operator ==(TwoDPoint lhs, TwoDPoint rhs) => lhs.Equals(rhs);
public static bool operator !=(TwoDPoint lhs, TwoDPoint rhs) => !(lhs == rhs);
}
class Program
{
static void Main(string[] args)
{
TwoDPoint pointA = new TwoDPoint(3, 4);
TwoDPoint pointB = new TwoDPoint(3, 4);
int i = 5;
// True:
Console.WriteLine($"pointA.Equals(pointB) = {pointA.Equals(pointB)}");
// True:
Console.WriteLine($"pointA == pointB = {pointA == pointB}");
// True:
Console.WriteLine($"object.Equals(pointA, pointB) = {object.Equals(pointA, pointB)}");
// False:
Console.WriteLine($"pointA.Equals(null) = {pointA.Equals(null)}");
// False:
Console.WriteLine($"(pointA == null) = {pointA == null}");
// True:
Console.WriteLine($"(pointA != null) = {pointA != null}");
// False:
Console.WriteLine($"pointA.Equals(i) = {pointA.Equals(i)}");
// CS0019:
// Console.WriteLine($"pointA == i = {pointA == i}");
// Compare unboxed to boxed.
System.Collections.ArrayList list = new System.Collections.ArrayList();
list.Add(new TwoDPoint(3, 4));
// True:
Console.WriteLine($"pointA.Equals(list[0]): {pointA.Equals(list[0])}");
// Compare nullable to nullable and to non-nullable.
TwoDPoint? pointC = null;
TwoDPoint? pointD = null;
// False:
Console.WriteLine($"pointA == (pointC = null) = {pointA == pointC}");
// True:
Console.WriteLine($"pointC == pointD = {pointC == pointD}");
TwoDPoint temp = new TwoDPoint(3, 4);
pointC = temp;
// True:
Console.WriteLine($"pointA == (pointC = 3,4) = {pointA == pointC}");
pointD = temp;
// True:
Console.WriteLine($"pointD == (pointC = 3,4) = {pointD == pointC}");
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/* Output:
pointA.Equals(pointB) = True
pointA == pointB = True
Object.Equals(pointA, pointB) = True
pointA.Equals(null) = False
(pointA == null) = False
(pointA != null) = True
pointA.Equals(i) = False
pointE.Equals(list[0]): True
pointA == (pointC = null) = False
pointC == pointD = True
pointA == (pointC = 3,4) = True
pointD == (pointC = 3,4) = True
*/
}
Yapılar için, Object.Equals(Object)'ın varsayılan uygulaması (bu, System.ValueType içindeki geçersiz kılınan sürümdür), türdeki her alanın değerlerini yansımayı kullanarak karşılaştırarak bir değer eşitliği denetimi gerçekleştirir. Bir uygulayıcı bir yapıdaki sanal Equals
yöntemi geçersiz kıldığında, amaç değer eşitliği denetimini gerçekleştirmek için daha verimli bir yöntem sağlamak ve isteğe bağlı olarak karşılaştırmayı yapı alanlarının veya özelliklerinin bazı alt kümesine dayandırmaktır.
== ve != işleçleri, yapı açıkça aşırı yüklemediği sürece bir yapı üzerinde çalışamaz.