C # ve Java neden '==' için referans eşitliği kullanıyor?


32

Java ve C # 'nın (ve diğer dillerin eminim) varsayılan olarak eşitlik için referans alması için bir süredir düşünmek üzereyim ==.

Yaptığım programlamada (kesinlikle yalnızca küçük bir programlama problemi alt kümesidir), referans eşitliği yerine nesneleri karşılaştırırken neredeyse her zaman mantıksal eşitlik istiyorum. Neden bu dillerin her ikisinin de bu rotaya tersine dönüp gitmek ==istediğini, mantıksal eşitlik ve .ReferenceEquals()referans eşitliği için kullanıldığını düşünmeye çalışıyordum .

Açıkçası referans eşitliğini kullanmak çok basittir ve çok tutarlı bir davranış sergilemektedir, ancak bugün gördüğüm programlama uygulamalarının çoğuna uymuyor gibi görünmektedir.

Mantıklı bir karşılaştırma yapmaya çalışmakla ilgili sorunlardan habersiz görünmek istemiyorum ve bunun her sınıfta uygulanması gerekiyor. Ayrıca, bu dillerin uzun zaman önce tasarlandığını da fark ettim, ancak genel soru duruyor.

Bunu kabul etmemin bazı önemli yararları basitçe eksik olduğumu mu yoksa varsayılan davranışın mantıksal eşitlik olması gerektiği ve bunun referans eşitliği için varsayılanın mantıklı bir eşitlik olması gerektiği makul görünüyor mu?


3
Değişkenler referans olduğu için? Değişkenler işaretçiler gibi hareket ettiğinden, onların benzer şekilde karşılaştırılması mantıklıdır
Daniel Gratzer

C #, yapılar gibi değer türleri için mantıksal eşitlik kullanır. Fakat “varsayılan mantıksal eşitlik” farklı referans türlerinden iki nesne için ne olmalıdır? Veya birinin B'den miras aldığı bir A tipi olduğu iki nesne için? Yapılar için olduğu gibi her zaman "yanlış"? Aynı cisme iki defa başvuruda bulunsanız bile, önce A, sonra B olarak? Bana anlamlı gelmiyor.
Doktor Brown,

3
Başka bir deyişle, neden C # 'da, geçersiz kılarsanız Equals()davranışının otomatik olarak değişmediğini mi soruyorsunuz ==?
svick

Yanıtlar:


29

C # yapar çünkü Java yaptı. Java, operatörün aşırı yüklenmesini desteklemediği için Java yaptı. Değer eşitliği her sınıf için yeniden tanımlanması gerektiğinden, bu bir operatör olamaz, bunun yerine bir yöntem olmalıydı. IMO bu kötü bir karardı. Çok daha hem yazma daha kolay ve okumak olduğunu a == bdaha a.equals(b)ve C veya C ++ deneyime sahip programcılar için çok daha doğal, ancak a == bhemen hemen her zaman yanlıştır. Gerekli olan ==yerin kullanımından kaynaklanan hatalar .equalssayısız programcı saatini boşa harcadı.


7
Sanırım operatörlerin aşırı yüklenme destekçisi olduğu kadar dedektörler de var, bu yüzden "kesin bir karardı" demeyeceğim. Örnek: Çalıştığım C ++ projesinde ==birçok sınıfa aşırı yükledik ve birkaç ay önce birkaç geliştiricinin ==aslında ne yaptığını bilmediğini keşfettim . Bazı yapıların anlamlarının açık olmadığı her zaman bu risk vardır. equals()Notasyonu Özel bir yöntemi kullanarak olduğumu ve ben bir yere bakmak zorunda olduğunu söylüyor. Alt satır: Operatör aşırı yüklenmesinin genel olarak açık bir konu olduğunu düşünüyorum.
Giorgio

9
Java'nın kullanıcı tanımlı bir operatör aşırı yüklemesi olmadığını söyleyebilirim . Java'da birçok operatörün anlamı (aşırı yüklenmiş). Bak +aynı anda ve dizeleri Birleştirme (sayısal değerler) eklenmesini yapar ki, mesela.
Joachim Sauer

14
a == bC, kullanıcı tanımlı operatör aşırı yüklenmesini desteklemediğinden C deneyimine sahip programcılar için daha doğal olabilir mi? (Örneğin, dizeleri karşılaştırmanın C yolu strcmp(a, b) == 0değildir a == b.)
svick

Bu temelde düşündüğüm şeydi, ama daha açık deneyime sahip olanlardan bir şeyi kaçırmamamı sağlamak için daha fazla tecrübeye sahip olanlara soracağımı düşündüm.
Fermuar

4
@svick: C, herhangi bir dize türü veya referans türü yoktur. Dize işlemleri ile yapılır char *. İki işaretçiyi eşitlik için karşılaştırmanın, dize karşılaştırması ile aynı olmadığı bana açık geliyor.
kevin cline

15

Kısa cevap: Tutarlılık

Sorunuzu doğru bir şekilde cevaplamak için, geriye doğru bir adım atıp, eşitliğin bir programlama dilinde ne anlama geldiğine bakmayı öneriyorum . Çeşitli dillerde kullanılan en az ÜÇ farklı olasılık vardır:

  • Referans eşitliği : a ve b aynı nesneye başvurursa, a = b'nin doğru olduğu anlamına gelir. A ve b'nin, a ve b'nin tüm özellikleri aynı olsa bile, farklı nesnelere gönderme yapması doğru olmaz.
  • Sığ eşitlik : a ve b'nin başvurduğu nesnelerin tüm özellikleri aynı olduğunda, a = b'nin gerçek olduğu anlamına gelir. Sığ eşitlik, iki nesneyi temsil eden bellek alanının bitsel olarak karşılaştırılmasıyla kolayca uygulanabilir. Lütfen referans eşitliği için sığ eşitliği ima ettiğini unutmayın.
  • Derin eşitlik : a ve b'deki her bir özellik aynı veya derinlemesine eşit ise, a = b'nin gerçek olduğu anlamına gelir. Lütfen derin eşitliğin hem referans eşitliği hem de sığ eşitlikten kaynaklandığını unutmayın. Bu anlamda, derin eşitlik en zayıf eşitlik şeklidir ve referans eşitlik en güçlü olanıdır.

Bu üç eşitlik türü sıklıkla kullanılırlar çünkü sıkça kullanılırlar: üç eşitlik kontrolünün tümü bir derleyici tarafından kolayca oluşturulabilir (derin eşitlik durumunda, derleyicinin bir yapının sınırsız döngüleri önlemek için etiket bitlerini kullanması gerekebilir). karşılaştırılabilir dairesel referanslara sahiptir). Ancak başka bir sorun var: bunların hiçbiri uygun olmayabilir.

Önemsiz olmayan sistemlerde, nesnelerin eşitliği genellikle derin ve referans eşitliği arasında bir şey olarak tanımlanır. İki nesneyi belirli bir bağlamda eşit olarak görmek isteyip istemediğimizi kontrol etmek için, bazı özelliklerin tamamen farklı bir şey olmasına izin verilebilirken, bazı özelliklerin hafızada ve diğerlerinde derin eşitlikle karşılaştırılması gerekebilir. Gerçekten seveceğimiz şey, genellikle literatürde semantik eşitlik olarak adlandırılan, gerçekten hoş bir diğeri olan “ileri bir eşitlik türüdür” . Etki alanımızdaki şeyler eşitse eşittir. =)

Böylece sorunuza geri dönebiliriz:

Bunu kabul etmemin bazı önemli yararları basitçe eksik olduğumu mu yoksa varsayılan davranışın mantıksal eşitlik olması gerektiği ve sınıf için mantıksal bir eşitlik yoksa referans eşitliğine geri dönülmesi makul görünüyor mu?

Herhangi bir dilde 'a == b' yazarken ne demek istiyoruz? İdeal olarak, her zaman aynı olmalıdır: Anlamsal eşitlik. Ama bu mümkün değil.

Ana düşüncelerden biri, en azından sayılar gibi basit tipler için, aynı değerin atanmasından sonra iki değişkenin eşit olmasını beklememizdir. Aşağıya bakınız:

var a = 1;
var b = a;
if (a == b){
    ...
}
a = 3;
b = 3;
if (a == b) {
    ...
}

Bu durumda, her iki ifadede de “eşittir b” olmasını bekliyoruz. Başka bir şey delilik olurdu. Dillerin çoğu (tümü değilse) bu sözleşmeyi izler. Dolayısıyla basit tiplerle (aka değerler) anlamsal eşitliği nasıl elde edeceğimizi biliyoruz. Nesnelerle, tamamen farklı bir şey olabilir. Aşağıya bakınız:

var a = new Something(1);
var b = a;
if (a == b){
    ...
}
b = new Something(1);
a.DoSomething();
b.DoSomething();
if (a == b) {
    ...
}

Biz ilk 'eğer' her zaman doğru olacağını bekliyoruz. Ama ikinci “eğer” de ne bekliyorsunuz? Bu gerçekten bağlıdır. 'Bir şey mi' a ve b'nin (anlamsal) eşitliğini değiştirebilir mi?

Anlamsal eşitlikle ilgili sorun, derleyici tarafından nesneler için otomatik olarak oluşturulamaması veya yapılan ödevlerden açıkça anlaşılmamasıdır . Kullanıcının anlamsal eşitliği tanımlaması için bir mekanizma sağlanmalıdır. Nesne yönelimli dillerde, bu mekanizma kalıtsal bir yöntemdir: eşittir . Bir parça OO kodu okuduğumuzda, tüm sınıflarda aynı yöntemi uygulayacak bir yöntem beklemiyoruz. Kalıtım ve aşırı yüke alışkınız.

Operatörler ile aynı davranışı bekliyoruz. 'A == b' gördüğünüzde, her durumda aynı eşitlik türünü beklemelisiniz (yukarıdaki 4'ten). Bu nedenle, tutarlılığı hedefleyen dil tasarımcıları her tür için referans eşitliği kullanmıştır. Bir programcının bir yöntemi geçersiz kılmamasına veya değiştirmemesine bağlı olmamalıdır.

Not: Dee dili Java ve C # 'dan biraz farklıdır: eşittir operatörü basit tipler için sığ eşitlik ve kullanıcı tanımlı sınıflar için semantik eşitlik anlamına gelir (= kullanıcıyla birlikte yatan işlemin uygulanması sorumluluğundadır - varsayılan ayar yoktur). Basit tipler için, sığ eşitlik her zaman anlamsal eşitlik olduğundan, dil tutarlıdır. Yine de ödediği bedel, eşittir operatörünün varsayılan olarak kullanıcı tanımlı türler için tanımsız olmasıdır. Bunu uygulamak zorundasın. Ve bazen bu çok sıkıcı.


2
When you see ‘a == b’ you should expect the same type of equality (from the 4 above) in all situations.Java dil tasarımcıları, nesneler için referans eşitliği ve ilkel değerler için semantik eşitlik kullandılar. Bunun doğru karar olduğu ya da bu kararın ==nesnelerin anlamsal eşitliği için aşırı yüklenmeye izin vermekten daha "tutarlı" olduğu açık değildir .
Charles Salvia,

İlkellerde de “referans eşitliği eşdeğeri” kullandılar. "İnt i = 3" kullandığınızda, sayı için işaretçi yoktur, bu nedenle referansları kullanamazsınız. Dizelerle, bir "tür" ilkel tür, daha belirgindir: == (referans eşitliği) kullanmak için ".intern ()" veya doğrudan bir atama (String s = "abc") kullanmanız gerekir.
Hbas

1
PS: C # ise dizeleri ile tutarlı değildi. Ve IMHO, bu durumda, bu çok daha iyi.
Hbas

@CharlesSalvia: In Java, eğer ave baynı tip, ifadesidir a==bolmadığını testler ave baynı şeyi tutun. Bunlardan biri # 291 nesnesine bir referans tutarsa ​​ve diğeri # 572 nesnesine bir referans tutarsa, aynı şeyi tutmazlar. İçindekiler nesne # 291 ve # 572 eşdeğer olabilir, ancak değişkenler kendilerini farklı şeyler tutun.
supercat,

2
@CharlesSalvia Gördüğünü a == bve ne yaptığını bilecek şekilde tasarlanmıştır . Aynı şekilde, a.equals(b)bir aşırı yüklenmenin olduğunu görebilir ve varsayabilirsiniz equals. Eğer a == bçağrılar a.equals(b)(eğer uygulandıysa) referans veya içerik ile karşılaştırılıyor mu? Hatırlamıyor musun A sınıfı kontrol etmelisiniz. Ne denildiğinden bile emin değilseniz, kod artık hızlı bir şekilde okunmaz. Aynı imzalı yöntemlere izin veriliyormuş gibi olur ve çağrılan yöntem mevcut kapsamın ne olduğuna bağlıdır. Bu tür programlar okumak imkansız olurdu.
Neil

0

Neden bu dillerin her ikisinin de bu rotaya tersine dönmek yerine neden gittiğini ve == mantıksal eşitlik elde edip .ReferenceEquals () 'in referans eşitliği için kullanıldığını düşünmeye çalışıyordum.

Çünkü ikinci yaklaşım kafa karıştırıcı olurdu. Düşünmek:

if (null.ReferenceEquals(null)) System.out.println("ok");

Bu kod yazdırılmalı mı "ok", yoksa atılmalı NullPointerExceptionmı?


-2

Java ve C # için yarar nesneye yönelik olmalarından kaynaklanmaktadır.

Bir itibaren bakış performans noktasında - kolay yazma koduna da artmış olmalıdır: cepten nesneleri oldukça büyük olabilir, bunun dikkate alınarak, referans eşitliğini kontrol çabuk olacağını, farklı nesneler ile temsil edilecek mantıksal olarak ayrı elemanları niyetinde beri.

Bir itibaren mantıksal açıdan - (. Ex boş == null mantıksal yorumlanır nasıl bu duruma göre farklılık gösterebilir?) Diğerine bir nesnenin eşitlik eşitlik için nesnenin özelliklerini karşılaştırarak olarak bariz olarak olmak zorunda değildir.

Bence neyin kaynaştığını, “referans eşitliği üzerinde her zaman mantıksal eşitlik istediğinizi” gözlemlersiniz. Dil tasarımcıları arasındaki fikir birliği muhtemelen bunun tam tersi. Geniş bir programlama deneyimi yelpazesinden yoksun olduğum için şahsen bunu değerlendirmeyi zor buluyorum. Kabaca, optimizasyon algoritmalarında referans eşitliği ve veri setlerini işlemede mantıksal eşitliği daha fazla kullanıyorum.


7
Referans eşitliğinin nesne yönelimi ile ilgisi yoktur. Aslında tam tersi: nesne yöneliminin temel özelliklerinden biri, aynı davranışa sahip nesnelerin ayırt edilemez olmasıdır. Bir nesne başka bir nesneyi taklit edebilmelidir. (Sonuçta, OO simülasyon için icat edildi!) Referans eşitliği, aynı davranışa sahip iki farklı nesneyi ayırt etmenize, simüle edilmiş bir nesneyle gerçek olanı ayırt etmenize olanak sağlar. Bu nedenle, Referans Eşitliği nesne yönelimini bozuyor . Bir OO programı olmamalıdır Referans Eşitlik kullanın.
Jörg W Mittag

@ JörgWMittag: Nesne yönelimli bir program yapmak için, X nesnesine, durumunun Y'ye eşit olup olmadığını sorma aracı (potansiyel olarak geçici bir koşul) ve ayrıca X nesnesine Y'ye eşit olup olmadığını soran bir araç olmasını gerektirir. [X, yalnızca durumunun Y'ye eşit olması garanti edildiğinde Y'ye eşittir]. Eşitlik ve devlet eşitliği için ayrı sanal yöntemlere sahip olmak iyi olurdu, ancak birçok tür için referans eşitsizliği eşitsizlik anlamına gelir ve bunu ispatlamak için sanal yöntem gönderimi için zaman harcamak için hiçbir neden yoktur.
supercat,

-3

.equals()değişkenleri içerikleriyle karşılaştırır. bunun yerine ==nesneleri içerikleriyle karşılaştırır ...

nesneleri kullanmak daha doğru .equals()


3
Varsayım yanlış. .equals (), .equals () 'in kodlandığı her şeyi yapar. Normalde içerik gereğidir, ancak olmak zorunda değildir. Ayrıca .equals () kullanmak daha doğru değil. Bu sadece başarmaya çalıştığınız şeye bağlıdır.
Fermuar
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.