C # ile Java'nın üçlü operatörü (? :) arasındaki fark


94

Ben bir C # acemiyim ve sadece bir sorunla karşılaşıyorum. Üçlü operatör ( ? :) ile çalışırken C # ve Java arasında bir fark vardır .

Aşağıdaki kod segmentinde, 4. satır neden çalışmıyor? Derleyici bir hata mesajı gösteriyor there is no implicit conversion between 'int' and 'string'. 5. satır da çalışmıyor. İkisi Listde nesne, değil mi?

int two = 2;
double six = 6.0;
Write(two > six ? two : six); //param: double
Write(two > six ? two : "6"); //param: not object
Write(two > six ? new List<int>() : new List<string>()); //param: not object

Ancak, aynı kod Java'da çalışır:

int two = 2;
double six = 6.0;
System.out.println(two > six ? two : six); //param: double
System.out.println(two > six ? two : "6"); //param: Object
System.out.println(two > six ? new ArrayList<Integer>()
                   : new ArrayList<String>()); //param: Object

C # 'da hangi dil özelliği eksik? Varsa neden eklenmedi?


4
Uyarınca msdn.microsoft.com/en-us/library/ty67wk28.aspx "first_expression ve second_expression türü ya aynı olmalıdır, ya da bir kapalı dönüştürme bir türden diğerine bulunması gerekir."
Egor

2
Bir süredir C # yapmadım ama alıntı yaptığınız mesaj bunu söylemiyor mu? Üçlü değerin iki dalı aynı veri türünü döndürmelidir. Ona bir int ve bir String veriyorsun. C # bir int'i otomatik olarak String'e nasıl dönüştüreceğini bilmiyor. Üzerine açık bir tür dönüşümü koymanız gerekir.
Jay

2
@ blackr1234 Çünkü intbir nesne değil. Bu bir ilkel.
Code-Apprentice

3
@ Code-Apprentice evet onlar gerçekten çok farklı - C # çalışma zamanı yeniden biçimlendirmesine sahip, bu nedenle listeler ilgisiz türlerdendir. Oh, ve isterseniz, bir miktar ilişki katmak için karışıma genel arayüz kovaryansı / kontraverians da atabilirsiniz;)
Lucas Trzesniewski

3
OP'nin yararı için: Java'da tür silme, bunun anlamı ArrayList<String>ve ArrayList<Integer>sadece ArrayListbayt kodunun içinde olması. Bu , çalışma zamanında tam olarak aynı türde göründükleri anlamına gelir . Görünüşe göre C # 'da farklı türler.
Code-Apprentice

Yanıtlar:


106

İçinden bakıldığında C 5. Dil Şartname bölüm 7.14: Koşullu Operatör şu görebiliriz:

  • X tipi X ve y tipi Y ise

    • X'ten Y'ye örtük bir dönüşüm (§6.1) varsa, ancak Y'den X'e değilse, o zaman Y koşullu ifadenin türüdür.

    • Y'den X'e örtük bir dönüştürme (§6.1) varsa, ancak X'ten Y'ye değilse, X, koşullu ifadenin türüdür.

    • Aksi takdirde, hiçbir ifade türü belirlenemez ve derleme zamanı hatası oluşur

Başka bir deyişle: x ve y'nin birbirine dönüştürülüp dönüştürülemeyeceğini bulmaya çalışır ve değilse bir derleme hatası oluşur. Bizim durumumuzda intve stringaçık veya örtük bir dönüşüme sahip olmadığımız için derlenmeyecek.

Bunu Java 7 Dil Spesifikasyonu bölüm 15.25: Koşullu Operatör ile karşılaştırın :

  • İkinci ve üçüncü işlenenler aynı türe sahipse (boş tip olabilir), o zaman bu koşullu ifadenin türüdür. ( HAYIR )
  • İkinci ve üçüncü işlenenlerden biri T ilkel tipindeyse ve diğerinin türü, T'ye kutu dönüşümü (§5.1.7) uygulamanın sonucuysa, koşullu ifadenin türü T'dir ( NO ).
  • İkinci ve üçüncü işlenenlerden biri boş türdeyse ve diğerinin türü başvuru türündeyse, koşullu ifadenin türü bu başvuru türüdür. ( HAYIR )
  • Aksi takdirde, ikinci ve üçüncü işlenenler sayısal türlere dönüştürülebilir (§5.1.8) türlere sahipse, birkaç durum söz konusudur: ( NO )
  • Aksi takdirde, ikinci ve üçüncü operandlar sırasıyla S1 ve S2 tiplerindedir. T1'in S1'e boks dönüşümünün uygulanmasından kaynaklanan tür olmasına izin verin ve T2, S2'ye boks dönüşümü uygulamasından kaynaklanan tür olsun.
    Koşullu ifadenin türü, yakalama dönüşümünün (§5.1.10) lub (T1, T2) (§15.12.2.7) 'e uygulanmasının sonucudur. ( EVET )

Ve bölüm 15.12.2.7'ye bakın. Gerçek Argümanlara Dayalı Çıkarsama Tür Argümanları, onunla birlikte gelen çağrı için kullanılan tür olarak hizmet verecek ortak bir ata bulmaya çalıştığını görebiliriz Object. Object olan çağrı işe yarar böylece kabul edilebilir bir argüman.


1
En az bir yönde örtük bir dönüşüm olması gerektiğini söyleyerek C # spesifikasyonunu okudum. Derleyici hatası, yalnızca her iki yönde de örtük dönüştürme olmadığında oluşur.
Code-Apprentice

1
Tabii ki, doğrudan spesifikasyonlardan alıntı yaptığınız için bu hala en eksiksiz cevaptır.
Code-Apprentice

Bu aslında sadece Java 5 ile değiştirildi (sanırım 6 olabilirdi). Bundan önce Java, C # ile tamamen aynı davranışa sahipti. Numaralandırmaların uygulanma şekli nedeniyle, bu muhtemelen Java'da C #'dan daha fazla sürprize neden oldu, ancak baştan beri iyi bir değişiklik oldu.
Voo

@Voo: FYI, Java 5 ve Java 6 aynı özellik sürümüne sahip ( The Java Language Specification , Third Edition), bu nedenle Java 6'da kesinlikle değişmedi.
ruakh

86

Verilen cevaplar iyi; Onlara, bu C # kuralının daha genel bir tasarım kılavuzunun sonucu olduğunu ekleyeceğim. Birkaç seçenekten birinden bir ifadenin türünü çıkarması istendiğinde, C # bunların en iyi benzersizini seçer . Yani, C # "Zürafa, Memeli, Hayvan" gibi bazı seçenekler verirseniz, o zaman koşullara bağlı olarak en genel olanı - Hayvan - veya en spesifik olanı - Zürafayı - seçebilir. Ama aslında kendisine verilen seçeneklerden birini seçmesi gerekiyor . C # asla "seçimlerim Kedi ve Köpek arasındadır, bu nedenle Hayvanın en iyi seçim olduğu sonucuna varacağım" demez. Bu verilen bir seçim değildi, bu yüzden C # onu seçemez.

Üçlü operatör durumunda C # daha genel bir int ve string türünü seçmeye çalışır, ancak ikisi de daha genel olanı seçmez. Nesne gibi ilk etapta tercih edilmeyen bir türü seçmek yerine, C # hiçbir türün çıkarılamayacağına karar verir.

Ayrıca bunun C # 'ın başka bir tasarım ilkesine uygun olduğunu da not ediyorum: bir şey yanlış görünüyorsa, geliştiriciye söyleyin. Dil "Ne demek istediğini tahmin edeceğim ve yapabiliyorsam karıştıracağım" demiyor. Dil, "Burada kafa karıştırıcı bir şey yazdığınızı düşünüyorum ve size bundan bahsedeceğim" diyor.

Ayrıca, C # dan nedeni olmadığına dikkat değişkene göre atanan değerin , onun yerine başka yönü. C # "bir nesne değişkenine atıyorsunuz, bu nedenle ifade nesneye dönüştürülebilir olmalıdır, bu nedenle öyle olduğundan emin olacağım" demez. Bunun yerine, C #, "bu ifadenin bir türü olmalı ve türün nesneyle uyumlu olduğu sonucuna varabilmeliyim" diyor. İfadenin tipi olmadığı için hata üretilir.


6
Neden ani kovaryans / kontraverans ilgisinin olduğunu merak ederek ilk paragrafı geçtim, sonra ikinci paragrafta daha fazla anlaşmaya başladım, sonra cevabı kimin yazdığını gördüm ... Daha önce tahmin etmeliydim. Örnek olarak 'Zürafa'yı tam olarak ne kadar kullandığınızı hiç fark ettiniz mi? Zürafaları gerçekten seviyor olmalısın.
Pharap

21
Zürafalar harika!
Eric Lippert

9
@Pharap: Tüm ciddiyetle, zürafayı bu kadar çok kullanmamın pedagojik nedenleri var. Her şeyden önce, zürafalar dünya çapında tanınmaktadır. İkincisi, söylemesi eğlenceli bir kelime ve o kadar tuhaf görünüyorlar ki unutulmaz bir görüntü. Üçüncüsü, örneğin "zürafa" ve "kaplumbağa" nın aynı analojide kullanılması, "bu iki sınıf, bir temel sınıfı paylaşsalar da, çok farklı özelliklere sahip çok farklı hayvanlardır" kuvvetli bir şekilde vurgulanır. Tip sistemleri hakkındaki sorularda genellikle türlerin mantıksal olarak çok benzer olduğu ve sezgilerinizi yanlış yola yönlendiren örnekleri vardır.
Eric Lippert

7
@Pharap: Yani soru genellikle "müşteri fatura sağlayıcısı fabrikalarının bir listesi neden fatura sağlayıcı fabrikaların bir listesi olarak kullanılamıyor?" ve sezgileriniz "evet, bunlar temelde aynı şey" diyor, ama "zürafalarla dolu bir oda neden memelilerle dolu bir oda olarak kullanılamaz?" dediğinizde? daha sonra "memelilerle dolu bir oda bir kaplan içerebilir, zürafalarla dolu bir oda yapamaz" demek çok daha sezgisel bir benzetme haline gelir.
Eric Lippert

6
@Eric Başlangıçta bu problemle karşılaştım int? b = (a != 0 ? a : (int?) null). Yandaki tüm bağlantılı sorularla birlikte bu soru da var . Bağlantıları izlemeye devam ederseniz, epeyce var. Buna karşılık, Java'nın yaptığı gibi gerçek dünyadaki bir sorunla karşılaşan birini hiç duymadım.
BlueRaja - Dany Pflughoeft

24

Jenerik kısımla ilgili olarak:

two > six ? new List<int>() : new List<string>()

C # 'da, derleyici sağ taraftaki ifade parçalarını bazı yaygın tiplere dönüştürmeye çalışır ; çünkü List<int>ve List<string>iki farklı inşa türleri vardır, biri diğerine dönüştürülemez.

Java'da, derleyici dönüştürmek yerine ortak bir süper tip bulmaya çalışır , bu nedenle kodun derlenmesi, joker karakterlerin ve tür silmenin örtük kullanımını içerir ;

two > six ? new ArrayList<Integer>() : new ArrayList<String>()

derleme türüne sahiptir ArrayList<?>(aslında, aynı zamanda olabilir ArrayList<? extends Serializable>veya ArrayList<? extends Comparable<?>>kullanım bağlamına bağlı olarak, her ikisi de ortak jenerik süper tiplerdir) ve çalışma zamanı türü raw ArrayList(ortak ham süper tip olduğu için).

Örneğin (kendiniz test edin) ,

void test( List<?> list ) {
    System.out.println("foo");
}

void test( ArrayList<Integer> list ) { // note: can't use List<Integer> here
                                 // since both test() methods would clash after the erasure
    System.out.println("bar");
}

void test() {
    test( true ? new ArrayList<Object>() : new ArrayList<Object>() ); // foo
    test( true ? new ArrayList<Integer>() : new ArrayList<Object>() ); // foo 
    test( true ? new ArrayList<Integer>() : new ArrayList<Integer>() ); // bar
} // compiler automagically binds the correct generic QED

Aslında da Write(two > six ? new List<object>() : new List<string>());çalışmıyor.
blackr1234

2
@ blackr1234 tam olarak: Diğer cevapların daha önce söylediğini tekrarlamak istemedim, ancak üçlü ifadenin bir türe göre değerlendirilmesi gerekiyor ve derleyici ikisinin "en düşük ortak paydasını" bulmaya çalışmayacak.
Mathieu Guindon

2
Aslında, bu tür silme ile ilgili değil, joker karakter türleri ile ilgili. Silinmeleri ArrayList<String>ve ArrayList<Integer>olacaktır ArrayList(ham tür), ancak bu üçlü operatör için çıkarılan tür ArrayList<?>(joker türü). Daha genel olarak, Java çalışma zamanının tür silme yoluyla jenerikleri uygulamasının , bir ifadenin derleme zamanı türü üzerinde hiçbir etkisi yoktur .
meriton

1
@vaxquis teşekkürler! Ben bir C # adamıyım, Java jenerikleri kafamı karıştırıyor ;-)
Mathieu Guindon

2
Aslında tipi two > six ? new ArrayList<Integer>() : new ArrayList<String>()IS ArrayList<? extends Serializable&Comparable<?>>Eğer türde bir değişkene atamak anlamına gelir ArrayList<? extends Serializable>yanı sıra türde bir değişkene ArrayList<? extends Comparable<?>>ama tabii, ArrayList<?>eşdeğerdir, ArrayList<? extends Object>yanı çalışır.
Holger

6

Hem Java hem de C # (ve diğer birçok dilde), bir ifadenin sonucunun bir türü vardır. Üçlü operatör durumunda, sonuç için değerlendirilen iki olası alt ifade vardır ve her ikisi de aynı türe sahip olmalıdır. Java söz konusu olduğunda, bir intdeğişken Integerotomatik kutulama ile dönüştürülebilir . Şimdi hem beri Integerve Stringgelen inherit Object, bunlar basit daralması dönüşüm ile aynı türe dönüştürülebilir.

Öte yandan, C # 'da an intilkeldir ve örtük stringveya başka bir dönüştürme yoktur object.


Cevap için teşekkürler. Autoboxing şu anda düşündüğüm şey. C # otomatik kutulamaya izin vermiyor mu?
blackr1234

2
C # 'da bir nesneye int kutulamak tamamen mümkün olduğundan bunun doğru olduğunu düşünmüyorum. Buradaki fark, inanıyorum ki, C # izin verilip verilmediğini belirlemede çok gevşek bir yaklaşım benimsiyor.
Jeroen Vannevel

9
Bunun otomatik boksla ilgisi yok, C # 'da olduğu gibi. Aradaki fark, Java'nın yaptığı sırada (yalnızca Java 5!) C # ortak bir süper tip bulmaya çalışmaz. Bunu 2 özel sınıfla denerseniz ne olacağını görmeye çalışarak kolayca test edebilirsiniz (her ikisi de açıkça nesneden miras alır). Ayrıca, ' intye örtük bir dönüşüm var object.
Voo

3
Ve nitpick'e biraz daha fazla: 'den' Integer/Stringe Objectdönüşüm daralan bir dönüşüm değil, tam tersi :-)
Voo

1
Integerve Stringayrıca uygulama Serializableve Comparablebunlardan birine atamaları yanı mesela çalışacaktır böylece Comparable<?> c=condition? 6: "6";veya List<? extends Serializable> l = condition? new ArrayList<Integer>(): new ArrayList<String>();yasal bir Java kodu vardır.
Holger

5

Bu oldukça basittir. String ve int arasında örtük bir dönüşüm yoktur. üçlü operatörün son iki işlenenin aynı türe sahip olması gerekir.

Deneyin:

Write(two > six ? two.ToString() : "6");

11
Soru, Java ve C # arasındaki farkın nedenidir. Bu soruya cevap vermiyor.
Jeroen Vannevel
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.