Eşitlik işleçleri - iki nesnenin eşit olup olmadığını test etme

== (eşitlik) ve != (eşitsizlik) işleçleri, işlenenlerinin eşit olup olmadığını denetler. İçerikleri eşit olduğunda değer türleri eşittir. İki değişken aynı depolama alanına başvurduğunda başvuru türleri eşittir.

C# dili başvuru belgesi, C# dilinin en son yayımlanan sürümünü gösterir. Ayrıca, yaklaşan dil sürümü için genel önizlemelerdeki özelliklere yönelik ilk belgeleri içerir.

Belgelerde ilk olarak dilin son üç sürümünde veya geçerli genel önizlemelerde sunulan tüm özellikler tanımlanır.

Tavsiye

Bir özelliğin C# dilinde ilk tanıtıldığı zamanları bulmak için C# dil sürümü geçmişi makalesine bakın.

is == karşı test ederken bir teste alternatif olarak desen eşleştirme işlecini kullanabilirsiniz. is işleci tüm değer ve başvuru türleri için varsayılan eşitlik semantiğini kullanır.

Eşitlik işleci ==

Eşitlik işleci==, işlenenleri eşitse, true aksi takdirde döndürürfalse.

Değer türleri eşitliği

Yerleşik değer türlerinin işlenenleri, değerleri eşitse eşittir:

int a = 1 + 2 + 3;
int b = 6;
Console.WriteLine(a == b);  // output: True

char c1 = 'a';
char c2 = 'A';
Console.WriteLine(c1 == c2);  // output: False
Console.WriteLine(c1 == char.ToLower(c2));  // output: True

Not

İşlenenlerden == işleçleri için işlemin sonucu olur<.><=>= Bu koşul, değerin NaN dahil olmak üzere doublediğer float (veya ) değerlerden büyük, küçük veya NaNbuna eşit olmadığı anlamına gelir. Daha fazla bilgi ve örnek için veya Double.NaN başvuru makalesine Single.NaN bakın.

Temel alınan tam sayı türünün karşılık gelen değerleri eşitse, aynı sabit listesi türünün iki işleneni eşittir.

Kullanıcı tanımlı yapı türleri varsayılan olarak işleci desteklemez == . İşleci == desteklemek için kullanıcı tanımlı bir yapıyı aşırı yüklemesi gerekir.

C# tanımlama kümeleri ve == için yerleşik desteğe != sahiptir. Daha fazla bilgi için, Tanımlama grubu türleri makalesinin Tanımlama Grubu eşitliği bölümüne bakın.

Başvuru türleri eşitliği

Varsayılan olarak, kayıtlar hariç başvuru türü işlenenler aynı nesneye başvuruda bulunurlarsa eşittir:

public class ReferenceTypesEquality
{
    public class MyClass
    {
        private int id;

        public MyClass(int id) => this.id = id;
    }

    public static void Main()
    {
        var a = new MyClass(1);
        var b = new MyClass(1);
        var c = a;
        Console.WriteLine(a == b);  // output: False
        Console.WriteLine(a == c);  // output: True
    }
}

Yukarıdaki örnekte gösterildiği gibi, kullanıcı tanımlı başvuru türleri varsayılan olarak işleci destekler == . Ancak, bir başvuru türü işleci aşırı == yükleyebilir. Bir başvuru türü işleci aşırı yüklerse == , bu türdeki iki başvurunun Object.ReferenceEquals aynı nesneye başvurup başvurmadığını denetlemek için yöntemini kullanın.

Kayıt türleri eşitliği

Kayıt türleri, varsayılan olarak değer eşitliği semantiği sağlayan ve == işleçlerini destekler!=. Diğer bir ifadeyle, her ikisi de null tüm alanların değerleri veya bunlara karşılık gelen değerler olduğunda ve otomatik olarak uygulanan özellikler eşit olduğunda iki kayıt işleneni eşittir.

public class RecordTypesEquality
{
    public record Point(int X, int Y, string Name);
    public record TaggedNumber(int Number, List<string> Tags);

    public static void Main()
    {
        var p1 = new Point(2, 3, "A");
        var p2 = new Point(1, 3, "B");
        var p3 = new Point(2, 3, "A");

        Console.WriteLine(p1 == p2);  // output: False
        Console.WriteLine(p1 == p3);  // output: True

        var n1 = new TaggedNumber(2, new List<string>() { "A" });
        var n2 = new TaggedNumber(2, new List<string>() { "A" });
        Console.WriteLine(n1 == n2);  // output: False
    }
}

Yukarıdaki örnekte gösterildiği gibi, başvuru türü üyelerinin eşitliği kendi eşitlik uygulamaları kullanılarak karşılaştırılır.

Dize eşitliği

her ikisi de aynı uzunlukta olduğunda veya her iki dizenulldize işleneni eşittir:

string s1 = "hello!";
string s2 = "HeLLo!";
Console.WriteLine(s1 == s2.ToLower());  // output: True

string s3 = "Hello!";
Console.WriteLine(s1 == s3);  // output: False

Dize eşitliği karşılaştırmaları büyük/küçük harfe duyarlı sıralı karşılaştırmalardır. Dize karşılaştırması hakkında daha fazla bilgi için bkz . C# dilinde dizeleri karşılaştırma.

Temsilci eşitliği

Her ikisi de aynı uzunlukta olduğunda ve her konumda eşit girişlere sahip olduğunda, aynı çalışma zamanı türündeki iki null işlenen eşittir:

Action a = () => Console.WriteLine("a");

Action b = a + a;
Action c = a + a;
Console.WriteLine(object.ReferenceEquals(b, c));  // output: False
Console.WriteLine(b == c);  // output: True

Önemli

Çağırma listesindeki eşit girdiler, alıcı dahil olmak üzere çağrıdaki tüm sabit parametreleri içerir. Alıcı, girdi çağrıldığında tarafından this temsil edilen bir nesnenin örneğidir.

var o1 = new object();
var o2 = new object();
var d1 = o1.ToString;
var d2 = o2.ToString;
Console.WriteLine(object.ReferenceEquals(d1, d2));  // output: False
Console.WriteLine(d1 == d2);  // output: False (different receivers)

Daha fazla bilgi için C# dil belirtiminin Temsilci eşitlik işleçleri bölümüne bakın.

Aşağıdaki örnekte gösterildiği gibi, anlamsal olarak özdeş lambda ifadelerini değerlendirmeden gelen temsilciler eşit değildir:

Action a = () => Console.WriteLine("a");
Action b = () => Console.WriteLine("a");

Console.WriteLine(a == b);  // output: False
Console.WriteLine(a + b == a + b);  // output: True
Console.WriteLine(b + a == a + b);  // output: False

Eşitsizlik işleci !=

Eşitsizlik işleci!=, işlenenleri eşit değilse ve true değilse döndürürfalse. Yerleşik türlerin işlenenleri için ifadex != y, ifadesiyle !(x == y)aynı sonucu verir. Tür eşitliği hakkında daha fazla bilgi için Eşitlik işleci bölümüne bakın.

Aşağıdaki örnekte işlecinin nasıl kullanılacağı gösterilmektedir != :

int a = 1 + 1 + 2 + 3;
int b = 6;
Console.WriteLine(a != b);  // output: True

string s1 = "Hello";
string s2 = "Hello";
Console.WriteLine(s1 != s2);  // output: False

object o1 = 1;
object o2 = 1;
Console.WriteLine(o1 != o2);  // output: True

Sınıf hiyerarşilerinde eşitlik

Kayıtlar el ile çalışma olmadan devralmayı doğru işler. Derleyici tarafından oluşturulan eşitlik hem çalışma zamanı türünü hem de bildirilen tüm özellikleri denetler, böylece simetri ve geçiş gereksinimlerini otomatik olarak karşılar. Amaç değer eşitliği olduğunda el ile korumasız hiyerarşiyi tercih record edin.

Önemli

Mümkün olduğunda kullanın record ; derleyici sizin için gerekli tüm eşitlik üyelerini oluşturur. El ile uygulama yalnızca türünüzün kayıt dışı bir sınıftan türetilmesi gerektiğinde veya engelleyen recordbaşka kısıtlamaları olduğunda gereklidir.

Bir tür kayıt türü olamadığında eşitliği kendiniz tanımlayın

Aşağıda, kayıt olmayan bir değer türü için el ile gerçekleştirilen en düşük uygulama aşağıdadır:

class Color : IEquatable<Color>
{
    public Color(int r, int g, int b)
    {
        R = r;
        G = g;
        B = b;
    }

    public int R { get; }
    public int G { get; }
    public int B { get; }

    public bool Equals(Color? other) =>
        other is not null && R == other.R && G == other.G && B == other.B;

    public override bool Equals(object? obj) => obj is Color other && Equals(other);
    public override int GetHashCode() => HashCode.Combine(R, G, B);
}

Uygulama üç gerekli üye sağlar: Equals(T?) çekirdek karşılaştırması olarak, override Equals(object?) nesne düzeyi çağrıları için ve override GetHashCode() karma tabanlı koleksiyonlar düzgün çalışır. HashCode.Combine , tarafından Equalskullanılan değerlerden bir karma oluşturan bir kitaplık yardımcısıdır. IEquatable<T> Uygulama (Equals(T?)aşırı yükleme) isteğe bağlıdır, ancak arayanlar zaten somut türe sahip olduğunda kutulamadan kaçınır.

ve !=öğesini de tanımladığınızda==, dil çift olarak gerektirir; CS0660 ve CS0661 uyarıları dört üyenin de tutarlı kalmasını anımsatır.

Yukarıdaki üç üye olduğu için değer Equals eşitliğini yansıtır, ancak == henüz hiçbir == işleç bildirilmemiş olduğundan kimliği test eder:

var red1 = new Color(255, 0, 0);
var red2 = new Color(255, 0, 0);

Console.WriteLine(red1.Equals(red2)); // => True
Console.WriteLine(red1 == red2);      // => False  (no == overload; identity check)

Doğru bir uygulamanın denklik sözleşmesini de karşılaması gerekir (, yve z değerlerinin null olmadığını varsayalımx):

  1. Esnek: x.Equals(x) döndürür true.
  2. Simetrik: x.Equals(y) ile aynı değeri y.Equals(x)döndürür.
  3. Geçişli: ve y.Equals(z) her ikisi de trueisex.Equals(y), olmalıdır x.Equals(z)true.
  4. Tutarlı: nesne değişmediği sürece aynı değeri döndürmek için x.Equals(y) ardışık çağrılar.
  5. Null davranış: x.Equals(null) döndürür false; x.Equals(y) null xolmayan bir üzerinde çağrıldığında oluşturmamalıdır.

Korumasız bir sınıf hiyerarşisindeki değer eşitliği, simetrik ve geçişli kuralları karşılamak için korumalı bir sınıftan daha fazla özen gerektirir. Tehlike, gönderimin IEquatable<T>.Equals(T? other) çalışma zamanı türünü değil değişkenin bildirilen türünü (değişken bildiriminde yazılan tür) takip etmesidir. OlmayanvirtualEquals(Shape? other) bir bildirirseShape, çalışma zamanında tutan olarak Shape yazılan bir Circle değişken çağrılır Shape.Equalsve özel alanları sessizce yoksayılırCircle. Farklı yarıçaplara sahip iki Circle nesne, bir Shape değişken üzerinden erişildiğinde eşit olarak karşılaştırılabilir.

Doğru desen iki işbirliği gereksinimi gerektirir: türetilmiş her sınıfın karşılaştırmayı genişletebilmesi için türetilmiş Equals yöntemi virtual oluşturun ve farklı çalışma zamanı türlerindeki nesnelerin hiçbir zaman eşit kabul edilmemesi için temel sınıf uygulamasına bir GetType() == other.GetType() koruma ekleyin.

Temel sınıf uygulaması

// Shape is an unsealed base class. Making Equals virtual and guarding with GetType()
// ensures a derived instance is never equal to an instance of a different runtime type.
class Shape : IEquatable<Shape>
{
    public string Color { get; }
    public Shape(string color) => Color = color;

    public override bool Equals(object? obj) => Equals(obj as Shape);

    // virtual so derived classes can override and augment the comparison
    public virtual bool Equals(Shape? other) =>
        other is not null &&
        GetType() == other.GetType() &&   // reject different runtime types
        Color == other.Color;

    // GetType() is included because equality requires matching runtime types
    public override int GetHashCode() => HashCode.Combine(GetType(), Color);

    public static bool operator ==(Shape? l, Shape? r) => l?.Equals(r) ?? r is null;
    public static bool operator !=(Shape? l, Shape? r) => !(l == r);
}

Önemli noktalar:

  • virtual typed Equals: Türetilen her sınıf, karşılaştırmayı kendi alanlarıyla artırmak için bu yöntemi geçersiz kılar.
  • GetType() guard: GetType() == other.GetType() bir Circle öğesinin aynı renge sahip bir Shape değerine eşitlenmesini engeller ve farklı türetilmiş türlerdeki nesnelerin birbirine eşit olmasını önler.
  • GetHashCode içerir GetType(): çünkü iki nesne yalnızca çalışma zamanı türleri eşleştiğinde eşit olduğundan, GetHashCode veri alanlarının yanı sıra çalışma zamanı türünü de karma olarak içermelidir. Burada atlanması GetType() ve HashSet<T>içinde yanlış davranışa Dictionary<TKey,TValue> neden olur.
  • == için temsilciler Equalsişleç ve yöntem eşitliğini tutarlı tutar.

Türetilmiş sınıf uygulaması

Alan ekleyen türetilmiş bir sınıf türü Equalsgeçersiz kılar, kendi türüne yayınlar, çağırır base.Equalsve sonra kendi alanlarını karşılaştırır:

class Circle : Shape
{
    public double Radius { get; }
    public Circle(string color, double radius) : base(color) => Radius = radius;

    public override bool Equals(object? obj) => Equals(obj as Shape);

    // Calls base.Equals to verify Color and runtime type, then adds Radius
    public override bool Equals(Shape? other) =>
        other is Circle c && base.Equals(c) && Radius == c.Radius;

    public override int GetHashCode() => HashCode.Combine(GetType(), Color, Radius);
}

base.Equals(c) korumayı GetType() zorlar ve paylaşılan alanları denetler. bağımsız değişken başka türetilmiş bir Shape tür olduğunda aracılığıyla other is Circle c atama işlemi hızlı başarısız olur.

Temel tür değişkeni aracılığıyla kullanım

Shape circle1 = new Circle("red", 5.0);
Shape circle2 = new Circle("red", 7.0);
Shape circle3 = new Circle("red", 5.0);
Shape shape1  = new Shape("red");

Console.WriteLine(circle1.Equals(circle2)); // => False  (Radius differs)
Console.WriteLine(circle1.Equals(circle3)); // => True
Console.WriteLine(circle1.Equals(shape1));  // => False  (different runtime types)

Korumalı sınıflar daha basittir

Bir sınıfı alt sınıfa sealed ayıramazsınız, bu nedenle derleme zamanı ve çalışma zamanı türleri her zaman kabul eder. Görevliye GetType() veya virtual merkeze ihtiyacınız yok. Bir IEquatable<T>tür kayıt olamazken eşitliği kendiniz uygulama bölümünde gösterilen desen, korumalı bir sınıf için doğru ve eksiksizdir.

İşleç aşırı yüklenebilirliği

kullanıcı tanımlı bir türdeki ve işleçlerini ==!=. Bu iki işleçlerden birini aşırı yüklerseniz, diğer işleci de aşırı yüklemelisiniz.

Kayıt türündeki == ve != işleçlerini açıkça aşırı yükleyemezsiniz. kayıt türü için ve == işleçlerinin != davranışını değiştirmek için aşağıdaki imzayla yöntemini uygulayınT:IEquatable<T>.Equals

public virtual bool Equals(T? other);

C# dili belirtimi

Daha fazla bilgi için C# dil belirtiminin İlişkisel ve tür testi işleçleri bölümüne bakın.

Kayıt türlerinin eşitliği hakkında daha fazla bilgi için C# dil belirtimininEşitlik üyeleri bölümüne bakın.

Ayrıca bkz.