C# Eşitlik karşılaştırmaları

Tavsiye

Bu makale, en az bir programlama dili bilen ve C# dilini öğrenen geliştiriciler için Temel Bilgiler bölümünün bir parçasıdır. Programlamaya yeni başladıysanız ilk olarak Başlangıç öğreticileri ile başlayın.

Başka bir dilden mi geliyorsunuz? Java'da nesneler üzerinde == ve JavaScript'te nesneler üzerinde ===, içeriği değil kimliği sınar. C# sınıfları varsayılan olarak aynı şekilde çalışır. Python'da, == varsayılan olarak __eq__ çağırır ve içeriği test eder; bu, C# records'ların karşılaştırılmasına benzer. C# yapıları da Equals çağrıldığında değere göre karşılaştırılır.

C# iki eşitlik türünü ayırt eder. Değer eşitliği , verileri eşleştiğinde iki örneğin eşit olduğu anlamına gelir. Başvuru eşitliği , iki değişkenin yalnızca bellekteki aynı nesneye işaret ettiğinde eşit olduğu anlamına gelir. Bu koşul kimlik olarak da adlandırılır. Değer türleri genellikle verileri karşılaştırır ve başvuru türleri genellikle kimliği karşılaştırır. Tür yazarları bu varsayılanları değiştirebilir, ancak bu zihinsel model özdeş görünen iki nesnenin eşit olarak kabul edilmediği veya bir değişken aracılığıyla mutasyonun başka bir değişkenin gördüklerini sessizce değiştirdiği ince hataları önler.

Değer türleri, başvuru türleri ve eşitlik varsayılanları

C# dilindeki her tür bir değer türü veya başvuru türüdür. Değer türü, verilerini doğrudan değişkende tutar. Başvuru türü bir nesneye başvuru içerir. Bir başvuru türü değişkenini başka bir değişkene atadığınızda, her iki değişken de aynı nesneye başvurur. Değer türleri ve başvuru türleri hakkında daha fazla bilgi için bkz . Tür sistemine genel bakış.

Varsayılan eşitlik davranışı genellikle tür türünü izler:

  • Yerleşik sayısal türler ve sabit listeleri değer türleridir. İki int değişken, sayısal değerleri eşleştiğinde eşittir.
  • Yapılar değer türleridir. Normal bir struct, Equals çağırdığınızda değer eşitliğini kullanır.
  • Demetler değer türleridir. İki demet, tüm eleman değerleri eşleştiğinde eşittir.
  • Sınıflar başvuru türleridir. Düz sınıf başvuru eşitliğini kullanır, bu nedenle == iki Equals değişkenin aynı nesneye işaret edip etmediğini test edin.

Bir sınıf başvuru eşitliğini kullanır. Aynı verilere sahip iki ayrı nesne eşit değildir, ancak aynı nesneye başvuran iki değişken eşittir:

var order1 = new Order(42, "Shoes");
var order2 = new Order(42, "Shoes");

Console.WriteLine(order1 == order2);               // => False
Console.WriteLine(order1.Equals(order2));          // => False
Console.WriteLine(ReferenceEquals(order1, order2)); // => False

Order order3 = order1;
Console.WriteLine(order1 == order3);               // => True

struct, Equals aracılığıyla değer eşitliğini gösterir. Alanları eşleştiğinde iki yapı örneği eşittir:

var pt1 = new Point(3, 4);
var pt2 = new Point(3, 4);

Console.WriteLine(pt1.Equals(pt2)); // => True

Düz yapılar önceden tanımlanmış == bir işleç almaz. p1 == p2 ifadesinin düz bir struct üzerinde yazılması, yalnızca struct kendi operator == öğesini bildiriyorsa derlenir. Bir yapı için işleç karşılaştırmalarına ihtiyacınız varsa, == ve != işleçlerini bir çift olarak tanımlayın ve bunları Equals ile GetHashCode ile tutarlı tutun.

Demetler de değer türleridir. Karşılık gelen tüm öğelerin değerleri eşleştiğinde iki demet eşittir. Adlandırılmış bir demetteki öğe adları yalnızca derleme zamanında sağlanan bir kolaylıktır ve karşılaştırma sırasında dikkate alınmaz. Yalnızca konumlar ve değerler önemlidir:

var t1 = (Name: "Grace", Role: "Engineer");
var t2 = (Name: "Grace", Role: "Engineer");

Console.WriteLine(t1 == t2); // => True

Demet söz dizimi ve ayrıştırma hakkında daha fazla bilgi için bkz. Demetler ve ayrıştırma.

Kimliği doğrudan test etmek için kullanın Object.ReferenceEquals

ReferenceEquals, bir türün Equals öğesini geçersiz kılıp == öğesine aşırı yükleme uygulamasından bağımsız olarak her zaman kimliği sınar. İki değişkenin tam olarak aynı nesneye işaret edip etmediğini onaylamanız gerektiğinde bunu kimlik tanılaması olarak kullanın:

var doc1 = new Document("Report");
var doc2 = new Document("Report");
var doc3 = doc1;

Console.WriteLine(ReferenceEquals(doc1, doc2)); // => False
Console.WriteLine(ReferenceEquals(doc1, doc3)); // => True

Yaygın bir kullanım, tam karşılaştırmayı atlamak için bir Equals geçersiz kılmasında yer almaktır: her iki argüman da aynı referans olduğunda, tek tek alanları kontrol etmeye gerek kalmadan her zaman eşit olurlar.

Note

Değişkenler arabirim olarak yazıldığında, ==arabirim değişkenlerinin aynı nesneye başvurup başvurmadığını denetler. Equals çağrısı hâlâ temel nesnenin uygulamasını çalıştırır.

Note

ReferenceEquals her iki bağımsız değişken de aynı değerleri içerse bile değer türlerini karşılaştırırken her zaman döndürür false . Bu davranış, değer türündeki her bağımsız değişken ReferenceEquals öğesine geçirildiğinde bağımsız olarak ayrı bir yığın nesnesine kutulandığı için oluşur.

Türler farklı eşitlik semantiği tanımlayabilir

Türler , varsayılan davranıştan farklı eşitlik semantiği tanımlayabilir. En yaygın neden değer eşitliğini uygulamaktır. Banka hesabı, envanterdeki ürün veya sistemdeki bir kullanıcı gibi verileri temsil eden bir tür oluşturursanız, eşit değerlerle aynı değerlere sahip örnekleri göz önünde bulundurun. Değer eşitliğini uygulamak için kayıt türlerini seçin ve derleyici sizin için gerekli tüm eşitlik üyelerini oluşturur.

Note

Dizeler sınıftır, ancak ==Equals kimlik değil dize içeriğini karşılaştırır.

  • ==: eşitlik işleci. Çoğu tür bu işleci birincil eşitlik denetimi olarak kullanır. Davranışı, türün yerleşik veya kullanıcı tanımlı == bir işleci olup olmadığına bağlıdır.
  • !=: eşitsizlik işleci. Bir tür kullanıcı tanımlı == işlecini tanımladığında, != işlecini de tanımlamalıdır.
  • Equals: Her tür tarafından devralınan bir sanal yöntem. Bir türün eşitlik semantiğini değiştirmek için bunu geçersiz kılabilirsiniz.
  • GetHashCode: karma tabanlı koleksiyonlar tarafından kullanılan bir sanal yöntem. İki değer eşit olduğunda, karma kodları da eşit olmalıdır.
  • ReferenceEquals: kimliği her zaman test eden statik bir yöntemdir.

Değer eşitliği için kayıtları kullanma

Tür bir kayıt olabiliyorsa, veri odaklı tür için değer eşitliği sağlamak üzere record değiştiricisini kullanın. Derleyici, bildirilen her özellik değerini karşılaştıran , Equalsve GetHashCode==/ üyeleri oluşturur.!=

A record class hala bir başvuru türüdür, ancak kimlik yerine değerleri karşılaştırır:

var person1 = new Person("Ada", "Lovelace");
var person2 = new Person("Ada", "Lovelace");

Console.WriteLine(person1 == person2);               // => True
Console.WriteLine(person1.Equals(person2));          // => True
Console.WriteLine(ReferenceEquals(person1, person2)); // => False

ReferenceEquals, person1 ile person2’nin bellekte farklı nesneler olduğunu doğrularken, == ile Equals ise derleyici tarafından oluşturulan eşitlik denetimi özellik değerlerini karşılaştırdığı için True döndürür.

Aynı derleyici oluşturma, record struct türleri için de geçerlidir:

var dim1 = new Dimension(1920, 1080);
var dim2 = new Dimension(1920, 1080);

Console.WriteLine(dim1 == dim2);      // => True
Console.WriteLine(dim1.Equals(dim2)); // => True

Kayıt türleri, kendi türleri için tüm eşitlik kümesini oluşturur. Hem record class hem de record struct türleri, Equals ve GetHashCode öğelerini geçersiz kılar. Ayrıca == ve != işleçlerinin yanı sıra, kayıt türü için tür belirtilmiş bir Equals metodu da oluştururlar. Bu nedenle, düz bir struct'nin aksine, bir record struct, == ve !='ü otomatik olarak destekler. Kayıt türleri ve bunların eşitlik semantiği hakkında daha fazla bilgi için bkz. Kayıtlar.

Referans türünde üyeler içeren kayıtlar

Kayıt eşitliği üyelerin kendi eşitlik semantiğini kullanır. Her özellik veya alan kendi Equals yöntemi kullanılarak karşılaştırılır. , stringveya DateTimegibi intçoğu skaler değer için bu yaklaşım, kayıt üyelerinin değerlerini karşılaştırır. Bu incelik, T[] veya List<T> gibi yaygın değiştirilebilir koleksiyonlarda ortaya çıkar: bu türler referansa göre karşılaştırılır; bu nedenle, aynı içeriğe sahip farklı liste nesneleri içeren iki kayıt örneği, otomatik olarak oluşturulan kayıt eşitliği tarafından eşit kabul edilmez.

var playlist1 = new Playlist("Chill", new List<string> { "Song A", "Song B" });
var playlist2 = new Playlist("Chill", new List<string> { "Song A", "Song B" });

Console.WriteLine(playlist1.Equals(playlist2));                        // => False (different List instances)
Console.WriteLine(playlist1.Tracks.SequenceEqual(playlist2.Tracks));   // => True

playlist1 ve playlist2 ayrı List<string> örneklerdir. İçerikleri eşleşse de, Equals, false döndürür.

Koleksiyon içeriğini yansıtmak için kayıt eşitliğine ihtiyacınız olduğunda birkaç seçeneğiniz vardır:

  • Kayıtta IEquatable<T> uygulayın ve koleksiyon üyeleri için Equals kullanacak şekilde Enumerable.SequenceEqual öğesini geçersiz kılın.
  • Değer eşitliği olan bir koleksiyon türü kullanın ; örneğin, özel IEqualityComparer<T> bir tür veya kendi Equals öğeleri karşılaştıran bir tür.
  • Kimlik etrafında tasarım: Kayıt saf değer yerine bir varlığı temsil ederse, koleksiyon üyeleri için başvuru eşitliği kasıtlı olabilir.

Important

Eşitliğin el ile uygulanması bugün C# dilinde nadirdir. Kayıtlar, ortak değer eşitliği senaryolarını otomatik olarak işler. Eşitliği el ile uygulamanız gerekiyorsa - örneğin, türünüzün record olmayan bir taban sınıftan türemesi gerektiği için - dil başvurusundaki bir tür record olamadığında eşitliği kendiniz uygulayın bölümüne bakın.

Ayrıca bakınız