Jestem nowicjuszem w C # i właśnie napotykam problem. Istnieje różnica między językiem C # a Javą, gdy mamy do czynienia z operatorem trójargumentowym ( ? :
).
W następującym segmencie kodu, dlaczego czwarta linia nie działa? Kompilator wyświetla komunikat o błędzie there is no implicit conversion between 'int' and 'string'
. Piąta linia również nie działa. Oba List
są obiektami, prawda?
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
Jednak ten sam kod działa w Javie:
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
Jakiej funkcji języka w C # brakuje? Jeśli tak, dlaczego nie jest dodany?
java
c#
ternary-operator
conditional-operator
blackr1234
źródło
źródło
int
obiekt nie jest obiektem. To prymitywne.ArrayList<String>
iArrayList<Integer>
staje się po prostuArrayList
w kodzie bajtowym. Oznacza to, że w czasie wykonywania wydają się być dokładnie tego samego typu . Najwyraźniej w C # są to różne typy.Odpowiedzi:
Przeglądając specyfikację języka C # 5 w sekcji 7.14: Operator warunkowy , widzimy:
Innymi słowy: próbuje znaleźć, czy x i y mogą zostać zamienione na siebie nawzajem, a jeśli nie, wystąpi błąd kompilacji. W naszym przypadku
int
istring
nie ma wyraźnej lub dorozumianej konwersji, więc nie będzie skompilować.Porównaj to ze specyfikacją języka Java 7 w sekcji 15.25: Operator warunkowy :
Patrząc na sekcję 15.12.2.7. Wnioskowanie o argumentach typu Na podstawie rzeczywistych argumentów widzimy, że próbuje znaleźć wspólnego przodka, który posłuży jako typ użyty do wywołania, z którym się kończy
Object
.Object
jest dopuszczalnym argumentem, więc wywołanie będzie działać.źródło
Podane odpowiedzi są dobre; Dodam do nich, że ta reguła języka C # jest konsekwencją bardziej ogólnych wytycznych projektowych. Gdy C # poprosi o wywnioskowanie typu wyrażenia na podstawie jednej z kilku opcji, wybiera najlepszą z nich . Oznacza to, że jeśli dasz C # kilka opcji, takich jak „Żyrafa, ssak, zwierzę”, może wybrać najbardziej ogólny - Zwierzę - lub może wybrać najbardziej szczegółowy - Żyrafa - w zależności od okoliczności. Ale musi wybrać jeden z wyborów, które mu faktycznie dano . C # nigdy nie mówi „moje wybory są między kotem a psem, dlatego wnioskuję, że najlepszym wyborem jest Animal”. To nie był dany wybór, więc C # nie może go wybrać.
W przypadku operatora trójskładnikowego C # próbuje wybrać bardziej ogólny typ int i string, ale żaden z nich nie jest typem bardziej ogólnym. Zamiast wybierać typ, który nie był wyborem w pierwszej kolejności, na przykład obiekt, C # decyduje, że nie można wywnioskować typu.
Zwracam również uwagę, że jest to zgodne z inną zasadą projektowania języka C #: jeśli coś wygląda nie tak, poinformuj o tym dewelopera. Język nie mówi: „Zgadnę, co miałeś na myśli i będę się przez to gmatwał, jeśli będę mógł”. Język mówi: „Myślę, że napisałeś tu coś zagmatwanego i powiem ci o tym”.
Zwracam również uwagę, że C # nie prowadzi rozumowania ze zmiennej do przypisanej wartości , ale raczej w przeciwnym kierunku. C # nie mówi „przypisujesz do zmiennej obiektu, dlatego wyrażenie musi być konwertowalne na obiekt, dlatego upewnię się, że tak jest”. Zamiast tego C # mówi „to wyrażenie musi mieć typ i muszę być w stanie wywnioskować, że typ jest zgodny z obiektem”. Ponieważ wyrażenie nie ma typu, generowany jest błąd.
źródło
int? b = (a != 0 ? a : (int?) null)
. Jest też to pytanie wraz ze wszystkimi powiązanymi pytaniami z boku. Jeśli nadal będziesz podążać za linkami, jest ich całkiem sporo. Dla porównania, nigdy nie słyszałem, żeby ktoś napotkał prawdziwy problem ze sposobem, w jaki robi to Java.Odnośnie części ogólnej:
W języku C # kompilator próbuje przekonwertować prawostronne części wyrażenia na typowy typ; od
List<int>
iList<string>
są dwoma różnymi skonstruowanymi typami, jednego nie można przekonwertować na drugi.W Javie kompilator próbuje znaleźć wspólny typ nadrzędny zamiast dokonywać konwersji, więc kompilacja kodu obejmuje niejawne użycie symboli wieloznacznych i wymazywanie typów ;
ma typ kompilacji
ArrayList<?>
(w rzeczywistości może to być równieżArrayList<? extends Serializable>
lubArrayList<? extends Comparable<?>>
, w zależności od kontekstu użycia, ponieważ oba są typowymi rodzajami nadrzędnymi) i typ czasu wykonywania surowegoArrayList
uruchomieniowego (ponieważ jest to wspólny nadtyp surowy).Na przykład (sprawdź to sam) ,
źródło
Write(two > six ? new List<object>() : new List<string>());
też nie działa.ArrayList<String>
iArrayList<Integer>
byłybyArrayList
(typ surowy), ale typ wywnioskowany dla tego operatora trójskładnikowego toArrayList<?>
(typ wieloznaczny). Mówiąc bardziej ogólnie, środowisko wykonawcze Java implementuje typy ogólne poprzez wymazywanie typów, nie ma wpływu na typ wyrażenia w czasie kompilacji .two > six ? new ArrayList<Integer>() : new ArrayList<String>()
jest,ArrayList<? extends Serializable&Comparable<?>>
co oznacza, że możesz przypisać go do zmiennej typu,ArrayList<? extends Serializable>
jak również zmiennej typuArrayList<? extends Comparable<?>>
, ale oczywiścieArrayList<?>
, co jest równoważneArrayList<? extends Object>
, również działa.Zarówno w Javie, jak i C # (i większości innych języków) wynik wyrażenia ma typ. W przypadku operatora trójskładnikowego istnieją dwa możliwe wyrażenia podrzędne obliczane dla wyniku i oba muszą mieć ten sam typ. W przypadku Javy
int
zmienna może zostać przekonwertowana naInteger
zmienną poprzez autoboxing. Ponieważ zarównoInteger
iString
dziedziczą zObject
, można je przekonwertować na ten sam typ przez prostą konwersję zawężającą.Z drugiej strony, w C #, an
int
jest prymitywem i nie ma niejawnej konwersji dostring
ani żadnej innejobject
.źródło
int
doobject
.Integer/String
naObject
nie jest konwersją zawężającą, jest dokładnie odwrotnie :-)Integer
aString
także zaimplementować,Serializable
aComparable
więc przypisania do któregokolwiek z nich również będą działać, np.Comparable<?> c=condition? 6: "6";
lubList<? extends Serializable> l = condition? new ArrayList<Integer>(): new ArrayList<String>();
są legalnym kodem Java.To jest całkiem proste. Nie ma niejawnej konwersji między ciągiem a int. operator trójskładnikowy wymaga, aby ostatnie dwa operandy miały ten sam typ.
Próbować:
źródło