(Nesne) 0 == (nesne) 0, ((nesne) 0). Eşit ((nesne) 0) 'dan neden farklı?


117

Aşağıdaki ifadeler neden farklı?

[1]  (object)0 == (object)0 //false
[2]  ((object)0).Equals((object)0) // true

Aslında [1] 'i tamamen anlayabiliyorum çünkü muhtemelen .NET çalışma zamanı boxtam sayı olacak ve bunun yerine referansları karşılaştırmaya başlayacak. Ama [2] neden farklı?


36
Tamam, şimdi bu sorunun cevabını anladığınıza göre, sonucunu tahmin ederek anlayışınızı kontrol edin: short myShort = 0; int myInt = 0; Console.WriteLine("{0}{1}{2}", myShort.Equals(myInt), myInt.Equals(myShort), myInt == myShort); Şimdi gerçeğe karşı kontrol edin. Tahminin doğru muydu? Değilse, tutarsızlığı açıklar mısınız?
Eric Lippert

1
@Star, tavsiye edilen okuma için bkz msdn.microsoft.com/en-us/library/vstudio/... geçerli aşırı için int16aka shortsonra bakmak Eşittir yöntemle msdn.microsoft.com/en-us/library/ms173105.aspx . Eric Lippert'in bulmacasını bozmak istemiyorum ama bu sayfaları okuduktan sonra çözmek oldukça kolay olacak.
Sam Skuce

2
Bunun java sorusu olduğunu sanıyordum; en azından Eşittir'deki 'E'yi görmeden önce.
seteropere

4
@seteropere Java aslında farklıdır: Java'daki otomatik kutulama nesneleri önbelleğe alır, dolayısıyla ((Integer)0)==((Integer)0)doğru olarak değerlendirilir.
Jules

1
Ayrıca deneyebilirsiniz IFormattable x = 0; bool test = (object)x == (object)x;. Yapı zaten bir kutudayken yeni bir boks yapılmaz.
Jeppe Stig Nielsen

Yanıtlar:


151

Çağrıların farklı davranmasının nedeni, çok farklı yöntemlere bağlanmalarıdır.

Durum ==, statik referans eşitlik operatörüne bağlanacaktır. intOluşturulan 2 bağımsız kutulu değer vardır, bu nedenle aynı referans değildirler.

İkinci durumda, örnek yöntemine bağlanırsınız Object.Equals. Bu, filtrelenecek sanal bir yöntemdir Int32.Equalsve bu, kutulu bir tamsayıyı kontrol eder. Her iki tamsayı değeri de 0 olduğundan eşittirler


==Vaka çağırmaz Object.ReferenceEquals. ceqBir referans karşılaştırması yapmak için basitçe IL komutunu üretir .
Sam Harwell

8
@ 280Z28 bu sadece derleyici onu satır içi olduğu için değil mi?
markmnl

@ 280Z28 Yani? Benzer bir durum, Boolean.ToString yönteminin, herkese açık olan Boolean.TrueString ve Boolean.FalseString'i döndürmek yerine, görünüşte işlevinin içinde kodlanmış dizeleri içermesidir. Alakasız; Mesele şu ==ki, ReferenceEquals(yine de Nesne üzerinde) ile aynı şeyi yapıyor . Sık kullanılan işlevlerde gereksiz dahili işlev çağrılarını önlemek için MS tarafında yalnızca dahili optimizasyon.
Nyerguds

6
C # Dil Spesifikasyonu, paragraf 7.10.6, şöyle der: Önceden tanımlanmış referans tipi eşitlik operatörleri: bool operator ==(object x, object y); bool operator !=(object x, object y);Operatörler, eşitlik veya eşit olmama için iki referansı karşılaştırmanın sonucunu döndürür. Sonucu belirlemek için yöntemin System.Object.ReferenceEqualskullanılması şart değildir . @Markmnl'e: Hayır, C # derleyicisi satır içi değil, bu jitter bazen yaptığı bir şeydir (ama bu durumda değil). Yani 280Z28 doğru, ReferenceEqualsyöntem aslında kullanılmıyor.
Jeppe Stig Nielsen

@JaredPar: Spesifikasyonun bunu söylemesi ilginç, çünkü dil aslında böyle davranmıyor. Yukarıdaki gibi tanımlanan operatörler ve değişkenler Cat Whiskers; Dog Fido; IDog Fred;(ilgili olmayan arayüzler ICatve ilgili IDogolmayan sınıflar için Cat:ICatve Dog:IDog) verildiğinde, karşılaştırmalar Whiskers==Fidove Whiskers==34yasal olacaktır (ilki yalnızca Whiskers ve Fido'nun her ikisi de boşsa doğru olabilir; ikincisi asla doğru olamaz ). Aslında, bir C # derleyicisi her ikisini de reddedecektir. mühürlenirse Whiskers==Fred;yasaklanır, değilse Catizin verilir.
supercat

26

İnt değerini 0(veya başka bir değer türünü) objectiçine çevirdiğinizde , değer kutu içine alınır . Her biri objectfarklı bir kutu (yani farklı bir nesne örneği) üretir. Türün ==operatörü objectbir referans karşılaştırması gerçekleştirir, bu nedenle sol taraf ve sağ taraf aynı örnek olmadığından yanlış döndürür.

Öte yandan, Equalssanal bir yöntem olan kullandığınızda, gerçek kutulu tipin uygulamasını kullanır, yani Int32.Equalsher iki nesne de aynı değere sahip olduğundan true döndürür.


18

==Operatör, statik olan sanal değildir. Her objectiki nesnenin çalışma zamanı türünden bağımsız olarak bir referans karşılaştırması yapacak olan sınıfın tanımladığı (`nesne, işlenenlerin derleme zamanı türüdür) tam kodu çalıştıracaktır .

EqualsYöntem, bir sanal örnek bir yöntemdir. objectSınıftaki kodda değil, (birinci) nesnenin gerçek çalışma zamanı türünde tanımlanan kodu çalıştıracaktır . Bu durumda, nesne intbir değer karşılaştırması yapacaktır, çünkü inttürün Equalsyöntemi için tanımladığı şey budur .


==Belirteci aslında iki overloadable birisi olan operatörler ve biri değildir temsil eder. İkinci işlecin davranışı, aşırı yüklenmeden (nesne, nesne) çok farklıdır.
2013

13

Equals()Yöntem sanaldır.
Bu nedenle, çağrı sitesine çevrildiğinde bile her zaman somut uygulamayı çağırır object. değere göre karşılaştırmayı intgeçersiz kılar Equals(), böylece değer karşılaştırması elde edersiniz.


10

== kullanın: Object.ReferenceEquals

Object.Equals değeri karşılaştırır.

object.ReferenceEqualsYöntem referanslar karşılaştırır. Bir nesneyi ayırdığınızda, bellek yığınındaki nesnenin verilerine ek olarak bellek konumunu belirten bir değer içeren bir referans alırsınız.

object.EqualsYöntem nesnelerin içeriği karşılaştırır. Önce object.ReferenceEquals gibi referansların eşit olup olmadığını kontrol eder. Ancak daha sonra eşitliği daha fazla test etmek için türetilmiş Eşittir yöntemlerini çağırır. Bunu gör:

   System.Object a = new System.Object();
System.Object b = a;
System.Object.ReferenceEquals(a, b);  //returns true

Object.ReferenceEqualsC # ==operatörünü işlenenlerinde kullanan bir yöntem gibi davranmasına rağmen , C # referans-eşitlik operatörü ( ==aşırı yük tanımlanmamış işlenen türleri kullanılarak temsil edilir ) çağırmak yerine özel bir talimat kullanır ReferenceEquals. Ayrıca, Object.ReferenceEqualsyalnızca her ikisi de boş olduğunda eşleşebilecek işlenenleri kabul edecek ve tip-zorlaması gereken Objectve bu nedenle herhangi bir şeyle eşleşemeyecek işlenenleri kabul edecek , referans eşitlik sürümü ise ==bu tür bir kullanımı derlemeyi reddedecektir. .
supercat

9

C # operatörü, ==iki farklı operatörü temsil etmek için jetonu kullanır : statik olarak aşırı yüklenebilir bir karşılaştırma operatörü ve aşırı yüklenemez bir referans karşılaştırma operatörü. Jetonla karşılaştığında, ==önce işlenen türleri için geçerli olan herhangi bir eşitlik testi aşırı yüklemesi olup olmadığını kontrol eder. Eğer öyleyse, bu aşırı yüklemeyi başlatacaktır. Aksi takdirde, türlerin referans karşılaştırma operatörü için geçerli olup olmadığını kontrol edecektir. Eğer öyleyse, o operatörü kullanacaktır. Operand türlerine hiçbir operatör uygulanamazsa, derleme başarısız olur.

Kod (Object)0bir hava bacası değil, sadece does Int32ToObject :, Int32tüm değer türleri gibi, aslında biri değerleri ve depolama konumlarını (değişmez sıfır gibi) açıklayan, ancak hiçbir şeyden türetmeyen ve biri açıklayan iki türü temsil eder. yığın nesneleri ve türetilir Object; yalnızca ikinci tür yukarı çevrilebileceğinden Object, derleyicinin bu ikinci türden yeni bir yığın nesnesi oluşturması gerekir. Her çağrılması (Object)0yeni bir yığın nesnesi oluşturur, böylece iki işlenen ==farklı nesnelerdir ve her biri bağımsız olarak Int320 değerini içerir.

Sınıf Object, eşittir operatörü için tanımlanmış herhangi bir kullanılabilir aşırı yüklemeye sahip değil. Sonuç olarak, derleyici aşırı yüklenmiş eşitlik testi işlecini kullanamayacak ve referans eşitlik testini kullanmaya geri dönecektir. İki işlenen ==farklı nesnelere atıfta bulunduğundan, rapor verecektir false. İkinci karşılaştırma başarılı olur çünkü bir yığın nesne örneğinin diğerine Int32eşit olup olmadığını sorar . Bu örnek, başka bir farklı örneğe eşit olmanın ne anlama geldiğini bildiği için yanıt verebilir true.


Ek olarak, 0kodunuza bir değişmez yazdığınız her seferinde, bunun için yığın içinde bir int nesnesi oluşturduğunu varsayıyorum. Bu, tek bir global statik sıfır değerine benzersiz bir referans değildir (sadece yeni dizeleri başlatmak için yeni boş dize nesneleri yapmaktan kaçınmak için String.Empty'yi nasıl yaptıkları gibi) Bu nedenle, a yapmanın bile 0.ReferenceEquals(0)false döndürdüğünden eminim , çünkü her iki 0 da yeni oluşturulan Int32nesneler.
Nyerguds

1
@Nyerguds, ints, heap, history, global statics vb. Hakkında söylediğin her şeyin yanlış olduğuna eminim 0.ReferenceEquals(0)çünkü bir derleme zamanı sabitinde bir yöntem çağırmaya çalışıyorsun. onu asacak bir nesne yok. Kutusuz int yığın üzerinde depolanan bir yapıdır. Hatta int i = 0; i.ReferenceEquals(...)çalışmayacak. Çünkü System.Int32miras ALMAZ Object.
Andrew Backer

@AndrewBacker, System.Int32a, structa, structolan System.ValueTypekendisi devraldığı, System.Object. Bunun için bir ToString()yöntem ve Equalsyöntem varSystem.Int32
Sebastian

1
Yine de Nyerguds, yığın üzerinde bir Int32 oluşturulacağını söylemesi yanlıştır, ki durum böyle değildir.
Sebastian

@SebastianGodelet, bunun içindekileri görmezden geliyorum. System.Int32 bu yöntemleri kendisi uygular. GetType () açık Objectve uzun zaman önce bunun için endişelenmeyi bıraktığım yer burası. Daha uzağa gitmek asla gerekli olmadı. AFAIK, CLR iki türü farklı ve özel olarak ele alır. Bu sadece miras değil. Yine isde iki veri türünden biri. Ben sadece kimsenin bu yorumu okumasını ve bu kadar yoldan sapmasını istemedim, boş dizgelerle ilgili tuhaflık da dahil olmak üzere, dizge stajını görmezden geliyor.
Andrew Backer

3

Her iki kontrol de farklı. Birincisi kimliği kontrol eder , ikincisi eşitlik için . Aynı nesneye atıfta bulunuyorlarsa, genel olarak iki terim aynıdır. Bu onların eşit oldukları anlamına gelir. Değerleri aynıysa iki terim eşittir.

Programlama açısından kimlik genellikle referans eşitliği ile karıştırılır. Her iki terimin göstericisi eşitse (!), İşaret ettikleri nesne tamamen aynıdır. Bununla birlikte, işaretçiler farklıysa, işaret ettikleri nesnelerin değeri yine de eşit olabilir. C # 'da kimlik statik Object.ReferenceEqualsüye kullanılarak kontrol edilebilirken, eşitlik statik olmayan Object.Equalsüye kullanılarak kontrol edilebilir . Eğer operatior, (Btw, "boks" olarak adlandırılır) nesnelere iki tamsayı döküm olduğundan ==arasındaobject gerçekleştirir varsayılan olarak ilk onay, eşlenen Object.ReferenceEqualsve kimlik denetler. Açıkça statik olmayan Equalsüyeyi çağırırsanız, dinamik göndermeInt32.Equals , eşitliği kontrol edenbir çağrıyla sonuçlanır.

Her iki kavram da benzerdir, ancak aynı değildir. İlk başta kafa karıştırıcı görünebilirler, ancak küçük fark çok önemlidir! İki kişiyi, yani "Alice" ve "Bob" hayal edin. İkisi de sarı bir evde yaşıyor. Alice ve Bob'un evlerin sadece renklerinin farklı olduğu bir semtte yaşadıkları varsayımına göre, ikisi de farklı sarı evlerde yaşayabilirler. Her iki evi de karşılaştırırsanız, kesinlikle aynı olduklarını, çünkü ikisinin de sarı olduğunu fark edeceksiniz! Ancak aynı evi paylaşmıyorlar ve bu nedenle evleri eşit ama aynı değil . Kimlik, aynı evde yaşadıkları anlamına gelir .

Not : Bazı diller, ===operatörü kimlik kontrolü için tanımlamaktadır .

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.