C # 'daki temel öğeler için == ve Eşittir () arasındaki fark nedir?


180

Bu kodu düşünün:

int age = 25;
short newAge = 25;
Console.WriteLine(age == newAge);  //true
Console.WriteLine(newAge.Equals(age)); //false
Console.ReadLine();

Hem intve shortbir karşılaştırma ilkel türleri vardır, ama ==geri döner doğru ve ile bir karşılaştırma Equalsyanlış geri döner.

Neden?


9
@OrangeDog Lütfen soruyu düşünün ve kapatmak için oy verin

4
Bu bariz ters girişim eksik:Console.WriteLine(age.Equals(newAge));
ANeves

3
Yinelenen bu davranışı açıklamaz; sadece Equals()genel olarak neyle ilgili .
SLaks

37
Bu soruyu birkaç gün önce Coverity blogunda yanıtladım. blog.coverity.com/2014/01/13/inconsistent-equality
Eric Lippert

5
@CodesInChaos: Spesifikasyon aslında "ilkel tipler" terimini hiç tanımlamadan iki kez kullanır; sonuç, ilkel türlerin yerleşik değer türleridir, ancak bu asla açıklığa kavuşturulmamıştır. Mads'e, kaldırdığından daha fazla karışıklık yaratıyor gibi göründüğü gibi, şartnameden sadece sertleşmesini tavsiye ettim.
Eric Lippert

Yanıtlar:


262

Kısa cevap:

Eşitlik karmaşıktır.

Ayrıntılı cevap:

Temel türler tabanı geçersiz kılar object.Equals(object)ve kutulu objectaynı tür ve değere sahipse true değerini döndürür . (Null olabilecek türler için de işe yarayacağını unutmayın; null olmayan null olabilecek türler her zaman temel alınan türün bir örneğine kutulanır.)

Yana newAgebir olduğunu short, onun Equals(object)bir kutulu geçirirseniz yöntem yalnızca true döndürür eteğine aynı değere sahip. Kutuyu geçiyorsunuz int, bu yüzden yanlış döndürüyor.

Aksine, ==operatör iki ints (veya shorts veya longs) almak olarak tanımlanır .
Bir ile aradığınızda intve short, derleyici örtülü dönüştürür shortiçin intve ortaya çıkan karşılaştırmak intdeğeriyle s.

Çalışmasını sağlamanın diğer yolları

İlkel türler de Equals()aynı türü kabul eden kendi yöntemlerine sahiptir.
Eğer yazarsanız age.Equals(newAge), derleyici seçecektir int.Equals(int)iyi aşırı olarak ve örtük dönüştürme shortiçin int. Daha sonra geri dönecektir true, çünkü bu yöntem ints'yi doğrudan karşılaştırır .

shortayrıca bir short.Equals(short)yöntemi vardır, ancak intörtük olarak dönüştürülemez short, bu nedenle çağırmazsınız.

Bu yöntemi oyuncu kadrosuyla çağırmaya zorlayabilirsiniz:

Console.WriteLine(newAge.Equals((short)age)); // true

Bu, short.Equals(short)boks olmadan doğrudan arayacaktır . Eğer age32767 büyükse, bu bir taşma özel durum oluşturur.

short.Equals(object)Aşırı yüklemeyi de çağırabilirsiniz , ancak kutulu bir nesneyi açıkça aynı türden alacak şekilde geçirebilirsiniz:

Console.WriteLine(newAge.Equals((object)(short)age)); // true

Önceki alternatif gibi, bu a short. Önceki çözümden farklı olarak, shortnesneyi zaman ve bellek harcayarak bir nesneye koyar.

Kaynak kodu:

Equals()Gerçek kaynak kodundan her iki yöntem de şöyledir :

    public override bool Equals(Object obj) {
        if (!(obj is Int16)) {
            return false;
        }
        return m_value == ((Int16)obj).m_value;
    }

    public bool Equals(Int16 obj)
    {
        return m_value == obj;
    }

Daha fazla okuma:

Bakınız Eric Lippert .


3
Diyoruz eğer @SLaks, long == int, intörtük dönüştürüldü longhakkı?
Selman Genç

1
Ve evet, hepsini denemeden yazdım.
SLaks

1
Bir değişiklik dahi, söz kodunda çok unutmayın int age = 25;etmek const int age = 25;, sonra da sonuç değişecektir. Bir örtük dönüştürme olmasıdır intiçin shortbu durumda var mıdır. Bkz. Örtülü sabit ifade dönüşümleri .
Jeppe Stig Nielsen

2
@SLaks evet ama cevabınızın "iletilen değer" ifadesi her iki şekilde de yorumlanabilir (geliştirici tarafından iletilen değer veya kutudan çıkarıldıktan sonra gerçekten CLR tarafından iletilen değer olarak). Burada cevapları henüz bilmeyen sıradan bir kullanıcı sanırım eski olarak okuyacaktır
JaredPar

2
@Rachel: Bunun doğru olmadığı dışında; Varsayılan == operatör referans ile bu referans türleri karşılaştırır. Değer türleri ve aşırı yüklenen türler ==için yoktur.
SLaks

55

Olmadığından hiçbir aşırı yük short.Equalsolduğunu bir kabul int. Bu nedenle, buna denir:

public override bool Equals(object obj)
{
    return obj is short && this == (short)obj;
}

objdeğil short.. bu nedenle, yanlıştır.


12

Sınavı geçtikten sonra inthiç shortlar Eşittir' geçmek object:

resim açıklamasını buraya girin Yani bu sahte kod şöyle çalışır:

return obj is short && this == (short)obj;


10

==eşit bir koşulu kontrol etmek için kullanılır, sadece 2 şeyi karşılaştırmak için bir operatör (boolean operatörü) olarak düşünülebilir ve burada veri tipi önemli değildir, çünkü bir tür döküm yapılması gerekir ve Equalseşit koşulu kontrol etmek için kullanılır , ancak bu durumda veri türleri aynı olmalıdır. N Eşittir bir operatör değil bir yöntemdir.

Aşağıda verdiğinizden alınan küçük bir örnek verilmiştir ve bu kısaca farkı açıklığa kavuşturacaktır.

int x=1;
short y=1;
x==y;//true
y.Equals(x);//false

yukarıdaki örnekte, X ve Y aynı değerlere yani 1'e sahiptir ve kullandığımızda ==, ==kısa tip derleyici tarafından int'e dönüştürüldüğünde ve sonuç verildiğinde true döndürecektir .

ve kullandığımızda Equalskarşılaştırma yapılır, ancak tür dökümü derleyici tarafından yapılmaz, bu nedenle false döndürülür.

Millet, lütfen yanılıyorsam bana bildirin.


6

Bir yöntem veya işleç bağımsız değişkeninin gerekli türde olmadığı birçok bağlamda, C # derleyicisi örtük bir tür dönüştürme gerçekleştirmeye çalışır. Derleyici tüm argümanları operatörlerini ve yöntemlerini örtük dönüşümler ekleyerek tatmin edebilirse, bazı durumlarda (özellikle eşitlik testleriyle!) Sonuçlar şaşırtıcı olsa da, bunu şikayetsiz olarak yapar.

Ayrıca, örneğin her tür değer türü intveya shortaslında bir tür değer ve bir tür nesne (*) tanımlamaktadır. Örtük dönüşümler, değerleri diğer değer türlerine dönüştürmek ve her türlü değeri karşılık gelen nesne türüne dönüştürmek için mevcuttur, ancak farklı nesne türleri örtük olarak birbirine dönüştürülemez.

Biri kullanıyorsa ==bir karşılaştırma operatörü shortve bir int, shortörtük bir dönüştürülecektir int. Sayısal değerini eşit ise int, intbu eşit olacaktır dönüştürüldü hangi intkarşılaştırıldığı edildiği. Bununla birlikte, Equalsbir yöntemi kısa bir yöntemle karşılaştırmak için kullanmaya çalışırsa, yöntemin intaşırı yüklenmesini karşılayacak tek örtülü dönüşüm Equals, karşılık gelen nesne türüne dönüştürme olacaktır int. Girilen shortnesneyle eşleşip eşleşmediği sorulduğunda, söz konusu nesnenin bir intdeğil de olduğunu gözlemleyecek shortve bu nedenle muhtemelen eşit olamayacağı sonucuna varacaktır.

Genel olarak, derleyici bundan şikayet etmese de, aynı tipte olmayan şeyleri karşılaştırmaktan kaçınmalıdır; bir şeylerin ortak bir forma dönüştürülmesinin aynı sonucu verip vermeyeceği ile ilgileniyorsa, bu dönüşümü açıkça gerçekleştirmelidir. Örneğin,

int i = 16777217;
float f = 16777216.0f;

Console.WriteLine("{0}", i==f);

Birini inta ile karşılaştırmak isteyebileceğiniz üç yol vardır float. Birisi bilmek isteyebilir:

  1. Maça mümkün olan en yakın floatdeğer ?intfloat
  2. floatMaçın tam sayı kısmı eşleşiyor intmu?
  3. Aynı sayısal değeri yapın intve floattemsil edin.

Bir intve floatdoğrudan karşılaştırmaya çalışırsa , derlenen kod ilk soruyu cevaplar; bununla birlikte programcının amaçladığı şey bu kadar açık değildir. Karşılaştırmanın değiştirilmesi, (float)i == filk anlamın amaçlandığını açıklığa kavuşturacak veya (double)i == (double)fkodun üçüncü soruyu cevaplamasına neden olacaktır (ve amaçlananın bu olduğunu açıklığa kavuşturacaktır).

(*) C # özelliği, türün değerini örneğin System.Int32bir nesne türü olarak görse bile System.Int32, bu tür bir görünüm, özellikleri farklı evrenlerde yaşayan değerleri ve nesneleri gören bir platformda kodun çalışmasıyla çelişir. Ayrıca, Tbir referans tipi ve xa ise T, o zaman bir tip referansı referans Tverebilmelidir x. Değişken Böylece, vÇeşidi Int32bir tutar Object, tip bir referans Objectbir referans tutmak mümkün olmalıdır vve içeriği. Aslında, bir tür referansı, kendisinden veya içeriğinden Objectkopyalanan v, ancak kopyalanan verileri tutan bir nesneye işaret edebilir v. Bu, hiçbirininvne de içeriği gerçekten bir Object.


1
the only implicit conversion which would satisfy an overload of the Equals method would be the conversion to the object type corresponding to intYanlış. Java'nın aksine, C # ayrı ilkel ve kutulu tiplere sahip değildir. Kutulu olduğu objectiçin diğer aşırı yük budur Equals().
SLaks

Birinci ve üçüncü soru aynıdır; dönüşümünde kesin değer zaten kayboldu float. A'ya bir döküm floatyapmak doublesihirli bir şekilde yeni bir hassasiyet yaratmaz.
SLaks

@SLaks: C #'ın üzerinde çalıştığı sanal makineyi tanımlayan ECMA spesifikasyonuna göre, her bir değer türü tanımı iki farklı tür oluşturur. C # spesifikasyonu, tipte bir saklama yeri List<String>.Enumeratorve bir yığın nesnesi içeriğinin aynı olduğunu söyleyebilir List<String>.Enumerator, ancak ECMA / CLI spesifikasyonu farklı olduklarını ve C # 'da kullanıldıklarında bile farklı davrandıklarını söyleyebilir.
SuperCat

@SLaks: Karşılaştırma işleminden önce ive fher birine dönüştürülürse double, eşitsiz olarak karşılaştırılan 16777217.0 ve 16777216.0 verir. Dönüştürme i floateşittir 16777216.0f verir f.
SuperCat

@SLaks: Depolama yeri türleri ile kutulu nesne türleri arasındaki farkın basit bir örneği için yöntemi göz önünde bulundurun bool SelfSame<T>(T p) { return Object.ReferenceEquals((Object)p,(Object)p);}. Bir değer türüne karşılık gelen kutulu nesne türü ReferenceEquals, bir kimlik koruma upcast yoluyla parametre türünü karşılayabilir ; ancak, depolama yeri türü kimlik korumalı olmayan bir dönüşüm gerektirir. A'ya döküm Tyapmak Uorijinalden başka bir şeye referans verirse T, bu bana a'nın Tgerçekten a olmadığını düşündürür U.
SuperCat

5

Equals () bir System.Object Sınıfı
Sözdizimi yöntemidir : Genel sanal bool Equals ()
İki nesnenin durumunu karşılaştırmak istiyorsak, Equals () yöntemini kullanmalıyız.

Yukarıda belirtildiği gibi cevaplar == operatörleri karşılaştırmak değerleri aynıdır.

Lütfen ReferenceEqual ile karıştırmayın

Referans Eşittir ()
Sözdizimi: public static bool ReferenceEquals ()
Belirtilen nesneler örneğinin aynı örneğe sahip olup olmadığını belirler


8
Bu soruya hiç cevap vermiyor.
SLaks

Örneklerle açıklanan SLAK'lar, bu yukarıdaki sorunun temelidir.
Sugat Mankar

4

Farkına varmanız gereken şey yapmanın ==her zaman bir yöntem çağırmasıdır. Çağıran ister sorudur ==ve Equals/ çağırarak aynı şeyleri yapıyor biter.

Referans türlerinde, ==referansların aynı olup olmadığını her zaman 1. kontrol eder ( Object.ReferenceEquals). Equalsdiğer yandan geçersiz kılınabilir ve bazı değerlerin eşit olup olmadığını kontrol edebilir.

EDIT: svick cevap ve SLaks yorum eklemek için, İşte bazı IL kodu

int i1 = 0x22; // ldc.i4.s ie pushes an int32 on the stack
int i2 = 0x33; // ldc.i4.s 
short s1 = 0x11; // ldc.i4.s (same as for int32)
short s2 = 0x22; // ldc.i4.s 

s1 == i1 // ceq
i1 == s1 // ceq
i1 == i2 // ceq
s1 == s2 // ceq
// no difference between int and short for those 4 cases,
// anyway the shorts are pushed as integers.

i1.Equals(i2) // calls System.Int32.Equals
s1.Equals(s2) // calls System.Int16.Equals
i1.Equals(s1) // calls System.Int32.Equals: s1 is considered as an integer
// - again it was pushed as such on the stack)
s1.Equals(i1) // boxes the int32 then calls System.Int16.Equals
// - int16 has 2 Equals methods: one for in16 and one for Object.
// Casting an int32 into an int16 is not safe, so the Object overload
// must be used instead.

Peki iki yöntemi int== call ile karşılaştırmak için hangi yöntem kullanılır ? İpucu: Bunun operator ==için bir yöntem yoktur Int32, ama bunun için birString yöntem vardır .
Ocak'ta

2
Bu soruya hiç cevap vermiyor.
SLaks

@SLaks: gerçekten int ve kısa karşılaştırma ile ilgili özel soruya cevap vermiyor, zaten cevapladınız. ==Sadece sihir yapmakla kalmayıp sonunda bir yöntemi çağırdığını açıklamanın ilginç olduğunu düşünüyorum (çoğu programcı muhtemelen hiçbir operatörü hiç uygulamadı / geçersiz kılmadı). Belki kendi cevabımı eklemek yerine sorunuza bir yorum ekleyebilirdim. Söylediklerimin alakalı olduğunu düşünüyorsanız, kendinizinkileri güncellemekten çekinmeyin.
user276648

Not o ==ilkel türleri üzerinde değil aşırı yüklenmiş bir operatör, bunlarla derler özgü bir dil özelliğidir ceqIL talimatı.
SLaks

3

== İlkel

Console.WriteLine(age == newAge);          // true

İlkel karşılaştırmada == operatör oldukça açık davranır, C # 'da birçok == operatör aşırı yükü vardır.

  • string == dize
  • int == int
  • uint == uint
  • uzun == uzun
  • çok daha fazlası

Yani bu durumda oradan örtülü hiçbir dönüşüm intiçin shortancak shortetmek intmümkündür. Böylece newAge int'e dönüştürülür ve her ikisi de aynı değeri tutarken true döndüren karşılaştırma yapılır. Yani şuna eşittir:

Console.WriteLine(age == (int)newAge);          // true

İlkelde Eşittir ()

Console.WriteLine(newAge.Equals(age));         //false

Burada Equals () yönteminin ne olduğunu görmeliyiz, kısa tip değişkenli Equals diyoruz. Yani üç olasılık var:

  • Eşittir (nesne, nesne) // nesneden statik yöntem
  • Eşittir (nesne) // nesneden sanal yöntem
  • Eşittir (kısa) // IEquatable.Equals uygular (kısa)

İlk tür burada değildir, çünkü int türünün yalnızca bir argümanıyla çağırdığımız argüman sayısı farklıdır. Üçüncü olarak da yukarıda belirtildiği gibi elimine edilir, int'in kısaca örtük dönüşümü mümkün değildir. Yani burada ikinci tip Equals(object)denir. Bu short.Equals(object):

bool Equals(object z)
{
  return z is short && (short)z == this;
}

Yani burada koşul test edildi z is short, z yanlış olan bir int olduğundan yanlış döndürür.

İşte Eric Lippert'in ayrıntılı makalesi

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.