C # और Java के टर्नरी ऑपरेटर के बीच अंतर (? :)


94

मैं एक सी # नौसिखिया हूं और मैं सिर्फ एक समस्या का सामना करता हूं। टर्नीरी ऑपरेटर ( ? :) के साथ काम करते समय C # और Java में अंतर होता है ।

निम्नलिखित कोड सेगमेंट में, 4th लाइन काम क्यों नहीं करती है? कंपाइलर एक त्रुटि संदेश दिखाता है there is no implicit conversion between 'int' and 'string'। 5 वीं पंक्ति भी काम नहीं करती है। दोनों ही Listवस्तुएं हैं, क्या वे नहीं हैं?

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

हालाँकि, जावा में समान कोड काम करता है:

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 # में कौन सी भाषा सुविधा गायब है? यदि कोई है, तो इसे क्यों नहीं जोड़ा गया है?


4
Msdn.microsoft.com/en-us/library/ty67wk28.aspx के अनुसार "या तो first_expression और second_expression का प्रकार समान होना चाहिए, या एक अंतर्निहित रूपांतरण एक प्रकार से दूसरे में मौजूद होना चाहिए।"
Egor

2
मैंने थोड़ी देर में C # नहीं किया है, लेकिन जो संदेश आपने कहा है, वह नहीं है? टर्नरी की दो शाखाओं को एक ही डेटा प्रकार को वापस करना होगा। आप इसे एक इंट और एक स्ट्रिंग दे रहे हैं। C # नहीं जानता कि कैसे एक स्वचालित रूप से एक स्ट्रिंग में एक स्वचालित रूप से कनवर्ट करें। आपको इस पर एक स्पष्ट प्रकार का रूपांतरण करना होगा।
Jay

2
@ blackr1234 क्योंकि intकोई वस्तु नहीं है। यह एक आदिम है।
कोड-अपरेंटिस

3
@ कोड-अपरेंटिस हां वे वास्तव में बहुत अलग हैं - सी # में रनटाइम रिवीजन है, इसलिए सूची असंबंधित प्रकार की हैं। ओह, और यदि आप रिश्ते के कुछ स्तर को शुरू करना चाहते हैं, तो आप सामान्य इंटरफ़ेस कोवरियन / कंट्रोवर्सी को भी मिश्रण में फेंक सकते हैं;)
लुकास ट्रेज़न्यूस्की

3
ओपी के लाभ के लिए: जावा में टाइप इरेज़र का अर्थ है कि ArrayList<String>और ArrayList<Integer>बस ArrayListबाईटकोड में हो जाना। इसका मतलब है कि वे रन-टाइम में बिल्कुल उसी प्रकार के प्रतीत होते हैं । C # में स्पष्ट रूप से वे विभिन्न प्रकार के होते हैं।
कोड-अपरेंटिस

जवाबों:


106

सी # 5 भाषा विनिर्देश खंड 7.14 के माध्यम से देखना : सशर्त संचालक हम निम्नलिखित देख सकते हैं:

  • अगर x में टाइप X है और y में Y टाइप है

    • यदि एक अंतर्निहित रूपांतरण (.16.1) एक्स से वाई तक मौजूद है, लेकिन वाई से एक्स तक नहीं है, तो वाई सशर्त अभिव्यक्ति का प्रकार है।

    • यदि एक अंतर्निहित रूपांतरण (.16.1) वाई से एक्स तक मौजूद है, लेकिन एक्स से वाई तक नहीं है, तो एक्स सशर्त अभिव्यक्ति का प्रकार है।

    • अन्यथा, कोई भी अभिव्यक्ति प्रकार निर्धारित नहीं किया जा सकता है, और एक संकलन-समय त्रुटि होती है

दूसरे शब्दों में: यह पता लगाने की कोशिश करता है कि क्या x और y को प्रत्येक अभिभावक में बदला जा सकता है या नहीं और यदि नहीं, तो संकलन त्रुटि होती है। हमारे मामले में intऔर stringइसका कोई स्पष्ट या निहित रूपांतरण नहीं है, इसलिए यह संकलन नहीं करेगा।

जावा 7 भाषा विनिर्देश खंड 15.25 के साथ इसके विपरीत : सशर्त संचालक :

  • यदि दूसरे और तीसरे ऑपरेंड में एक ही प्रकार है (जो अशक्त प्रकार हो सकता है), तो वह सशर्त अभिव्यक्ति का प्रकार है। ( नहीं )
  • यदि दूसरे और तीसरे संचालकों में से एक आदिम प्रकार टी का है, और दूसरे का प्रकार टी में बॉक्सिंग रूपांतरण (conversion5.1.7) को लागू करने का परिणाम है, तो सशर्त अभिव्यक्ति का प्रकार टी है ( सं ।) ) है
  • यदि दूसरे और तीसरे ऑपरेंड में से एक अशक्त प्रकार का है और दूसरे का प्रकार एक संदर्भ प्रकार है, तो सशर्त अभिव्यक्ति का प्रकार वह संदर्भ प्रकार है। ( नहीं )
  • अन्यथा, यदि दूसरे और तीसरे ऑपरेंड के प्रकार हैं जो कि परिवर्तनीय (.85.1.8) से संख्यात्मक प्रकार के हैं, तो ऐसे कई मामले हैं (- NO )
  • अन्यथा, दूसरे और तीसरे ऑपरेंड क्रमशः S1 और S2 के प्रकार हैं। बता दें कि T1 S1 के बॉक्सिंग रूपांतरण को लागू करने से होने वाला परिणाम है, और T2 वह प्रकार है जो बॉक्सिंग रूपांतरण को S2 में लागू करने से उत्पन्न होता है।
    सशर्त अभिव्यक्ति का प्रकार लुब (टी 1, टी 2) ()15.12.2.7) पर कब्जा रूपांतरण (.15.1.10) को लागू करने का परिणाम है। ( हाँ )

और, खंड 15.12.2.7 देख रहे हैं वास्तविक तर्क के आधार पर प्रकार के तर्क का उल्लेख करते हुए हम यह देख सकते हैं कि यह एक सामान्य पूर्वज को खोजने की कोशिश करता है जो उस कॉल के लिए उपयोग किए जाने वाले प्रकार के रूप में काम करेगा जो इसके साथ लैंड करता है ObjectObject है एक स्वीकार्य तर्क तो कॉल काम करेंगे।


1
मैंने C # विनिर्देश को यह कहते हुए पढ़ा कि कम से कम एक दिशा में एक अंतर्निहित रूपांतरण होना चाहिए। कंपाइलर त्रुटि केवल तब होती है जब किसी भी दिशा में कोई अंतर्निहित रूपांतरण नहीं होता है।
कोड-अपरेंटिस

1
बेशक, यह अभी भी सबसे पूर्ण जवाब है क्योंकि आप सीधे विनिर्देशों से उद्धृत करते हैं।
कोड-अपरेंटिस

यह वास्तव में केवल जावा 5 के साथ बदला गया था (मुझे लगता है, 6 हो सकता है)। इससे पहले जावा का C # जैसा ही व्यवहार था। जिस तरह से एनमों को लागू किया गया है, शायद यह जावा में सी # की तुलना में अधिक आश्चर्यचकित करता है, हालांकि सभी के साथ एक अच्छा बदलाव।
वू

@Voo: FYI करें, जावा 5 और जावा 6 का एक ही कल्पना संस्करण है ( जावा भाषा विनिर्देश , तीसरा संस्करण), इसलिए यह निश्चित रूप से जावा 6 में नहीं बदला है
21

86

दिए गए उत्तर अच्छे हैं; मैं उनके साथ जोड़ना चाहूंगा कि C # का यह नियम अधिक सामान्य डिजाइन दिशानिर्देश का परिणाम है। जब कई विकल्पों में से एक से अभिव्यक्ति के प्रकार का अनुमान लगाने के लिए कहा जाता है, तो C # उनमें से सबसे अच्छा विकल्प चुनता है । यही है, अगर आप "जिराफ, स्तनपायी, पशु" जैसे कुछ # विकल्प देते हैं, तो यह सबसे सामान्य - पशु चुन सकता है - या यह परिस्थितियों के आधार पर सबसे विशिष्ट - जिराफ - चुन सकता है। लेकिन इसे उन विकल्पों में से एक को चुनना होगा जो वास्तव में दिया गया था । C # कभी नहीं कहता कि "मेरी पसंद कैट और डॉग के बीच है, इसलिए मैं यह कहूँगा कि एनिमल सबसे अच्छा विकल्प है"। यह एक विकल्प नहीं दिया गया था, इसलिए C # इसे नहीं चुन सकता है।

टर्नरी ऑपरेटर के मामले में C # अधिक सामान्य प्रकार के int और string को चुनने की कोशिश करता है, लेकिन न तो अधिक सामान्य प्रकार है। एक प्रकार चुनने के बजाय जो पहली जगह में एक विकल्प नहीं था, जैसे ऑब्जेक्ट, C # यह तय करता है कि किसी भी प्रकार का अनुमान नहीं लगाया जा सकता है।

मैं यह भी ध्यान देता हूं कि यह C # के एक अन्य डिज़ाइन सिद्धांत के साथ है: यदि कुछ गलत दिखता है, तो डेवलपर को बताएं। भाषा यह नहीं कहती है "मैं अनुमान लगाने जा रहा हूं कि आपका क्या मतलब है और यदि मैं कर सकता हूं तो उस पर कीचड़ उछालें"। भाषा कहती है, "मुझे लगता है कि आपने यहां कुछ भ्रमित किया है, और मैं आपको इसके बारे में बताने जा रहा हूं।"

इसके अलावा, मैं ध्यान देता हूं कि C # वेरिएबल से असाइन किए गए वैल्यू का कारण नहीं है , बल्कि दूसरी दिशा है। C # यह नहीं कहता है कि "आप ऑब्जेक्ट वेरिएबल को असाइन कर रहे हैं इसलिए एक्सप्रेशन को ऑब्जेक्ट में कन्वर्टिबल होना चाहिए, इसलिए मैं यह सुनिश्चित करूँगा कि यह है"। बल्कि, C # का कहना है कि "इस अभिव्यक्ति का एक प्रकार होना चाहिए, और मुझे यह कटौती करने में सक्षम होना चाहिए कि प्रकार वस्तु के साथ संगत है"। चूंकि अभिव्यक्ति में एक प्रकार नहीं है, इसलिए एक त्रुटि उत्पन्न होती है।


6
मुझे पहले पैराग्राफ से आश्चर्य हुआ कि अचानक कोविरियस / कंट्रोवर्सीज इंटरेस्ट क्यों, फिर मैं दूसरे पैराग्राफ पर अधिक सहमत होने लगा, फिर मैंने देखा कि किसने उत्तर लिखा है ... जल्द ही अनुमान लगाना चाहिए था। क्या आपने कभी गौर किया है कि आप उदाहरण के रूप में 'जिराफ' का कितना उपयोग करते हैं ? आप वास्तव में जिराफ की तरह होना चाहिए।
छपरा

21
जिराफ कमाल के हैं!
एरिक लिपर्ट

9
@ चरण: सभी गंभीरता में, मेरे पास जिराफ़ का उपयोग करने के लिए शैक्षणिक कारण हैं। सबसे पहले, जिराफ दुनिया भर में प्रसिद्ध हैं। दूसरा, यह एक तरह का मजेदार शब्द है, और वे इतने अजीब हैं कि यह एक यादगार छवि है। तीसरा, एक ही सादृश्य में "जिराफ़" और "कछुए" का उपयोग करते हुए, दृढ़ता से "इन दो वर्गों पर जोर देते हैं, हालांकि वे एक आधार वर्ग साझा कर सकते हैं, बहुत अलग विशेषताओं वाले बहुत अलग जानवर हैं"। टाइप सिस्टम के बारे में प्रश्न अक्सर ऐसे उदाहरण होते हैं जहां प्रकार तार्किक रूप से बहुत समान होते हैं, जो आपके अंतर्ज्ञान को गलत तरीके से चलाते हैं।
एरिक लिपर्ट

7
@ प्रिंट: यह सवाल आमतौर पर कुछ ऐसा है "ग्राहक चालान प्रदाता कारखानों की सूची का उपयोग चालान प्रदाता कारखानों की सूची के रूप में क्यों नहीं किया जा सकता है?" और आपका अंतर्ज्ञान कहता है "हाँ, वे मूल रूप से एक ही बात हैं", लेकिन जब आप कहते हैं कि "जिराफ से भरे कमरे को स्तनधारियों से भरे कमरे के रूप में इस्तेमाल क्यों नहीं किया जा सकता है?" तब यह कहना अधिक सहज ज्ञान युक्त उपमा बन जाता है "क्योंकि स्तनधारियों से भरे कमरे में बाघ हो सकता है, जिराफ से भरा कमरा नहीं हो सकता"।
एरिक लिपिपर्ट

6
@ मैं मूल रूप से इस समस्या में भाग गया int? b = (a != 0 ? a : (int?) null)। पक्ष में सभी जुड़े सवालों के साथ, यह सवाल भी है । यदि आप लिंक का अनुसरण जारी रखते हैं, तो काफी कुछ है। इसकी तुलना में, मैंने कभी भी किसी वास्तविक दुनिया के मुद्दे पर नहीं सुना है जिस तरह से जावा करता है।
ब्लूराजा - डैनी पफ्लुघोफ्ट

24

जेनरिक भाग के बारे में:

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

सी # में, संकलक दाएं हाथ के अभिव्यक्ति भागों को कुछ सामान्य प्रकार में बदलने की कोशिश करता है ; चूंकि List<int>और List<string>दो अलग-अलग प्रकार के निर्माण हैं, एक को दूसरे में परिवर्तित नहीं किया जा सकता है।

जावा में, संकलक परिवर्तित करने के बजाय एक सामान्य सुपरटेप को खोजने की कोशिश करता है , इसलिए कोड के संकलन में वाइल्डकार्ड और प्रकार के क्षरण के निहित उपयोग शामिल हैं ;

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

का संकलन प्रकार है ArrayList<?>(वास्तव में, यह भी हो सकता है ArrayList<? extends Serializable>या ArrayList<? extends Comparable<?>>, उपयोग के संदर्भ पर निर्भर करता है, क्योंकि वे दोनों सामान्य जेनेरिक सुपरटेप्स हैं) और रनटाइम प्रकार के कच्चे ArrayList(क्योंकि यह आम कच्चा सुपरटेप है)।

उदाहरण के लिए (इसे स्वयं परखें) ,

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

वास्तव में Write(two > six ? new List<object>() : new List<string>());या तो काम नहीं करता है।
blackr1234 19

2
@ blackr1234 बिल्कुल: मैं दोहराना नहीं चाहता था कि दूसरे उत्तर पहले से ही क्या कहते हैं, लेकिन टर्नरी अभिव्यक्ति को एक प्रकार का मूल्यांकन करने की आवश्यकता है, और कंपाइलर दोनों के "सबसे कम सामान्य भाजक" को खोजने का प्रयास नहीं करेगा।
मैथ्यू गुइंडन

2
दरअसल, यह टाइप इरेज़र के बारे में नहीं है, बल्कि वाइल्डकार्ड प्रकारों के बारे में है। का मिटना ArrayList<String>और ArrayList<Integer>होगा ArrayList(कच्चा प्रकार), लेकिन इस टर्नरी ऑपरेटर के लिए अनुमान प्रकार ArrayList<?>(वाइल्डकार्ड प्रकार) है। अधिक आम तौर पर, कि जावा रनटाइम जेनरेशन टाइप इरेज़र के माध्यम से जेनरिक लागू करता है, अभिव्यक्ति के संकलन समय प्रकार पर कोई प्रभाव नहीं पड़ता है ।
मेरिटॉन

1
@vaxquis धन्यवाद! मैं एक C # आदमी हूं, जावा जेनरिक मुझे भ्रमित करता है ;-)
मैथ्यू गुइंडन

2
वास्तव में, के प्रकार two > six ? new ArrayList<Integer>() : new ArrayList<String>()है ArrayList<? extends Serializable&Comparable<?>>कि आप टाइप के एक चर करने के लिए इसे असाइन कर सकते हैं, जिसका मतलब है ArrayList<? extends Serializable>और साथ ही प्रकार का एक चर ArrayList<? extends Comparable<?>>, लेकिन निश्चित रूप से ArrayList<?>है, जो के बराबर है ArrayList<? extends Object>, साथ ही काम करता है।
होल्गर

6

जावा और सी # (और अधिकांश अन्य भाषाओं) में, एक अभिव्यक्ति के परिणाम में एक प्रकार होता है। टर्नेरी ऑपरेटर के मामले में, परिणाम के लिए मूल्यांकन के दो संभावित उपप्रकार हैं और दोनों का एक ही प्रकार होना चाहिए। जावा के मामले में, एक intचर को Integerऑटोबॉक्सिंग द्वारा परिवर्तित किया जा सकता है । अब चूंकि दोनों Integerऔर से Stringविरासत में मिलते हैं Object, इसलिए उन्हें साधारण संकीर्ण रूपांतरण द्वारा एक ही प्रकार में परिवर्तित किया जा सकता है।

दूसरी ओर, C # में, intएक आदिम है और इसमें stringया किसी अन्य के लिए अंतर्निहित रूपांतरण नहीं है object


जवाब के लिए धन्यवाद। ऑटोबॉक्सिंग वह है जो मैं वर्तमान में सोच रहा हूं। क्या C # ऑटोबॉक्सिंग की अनुमति नहीं देता है?
अश्वेत 1234

2
मुझे नहीं लगता कि यह सही है क्योंकि किसी वस्तु के लिए एक बॉक्सिंग सी # में पूरी तरह से संभव है। यहाँ अंतर यह है कि, मेरा मानना ​​है कि C # यह निर्धारित करने के लिए एक बहुत ही उपयोगी दृष्टिकोण लेता है कि इसकी अनुमति है या नहीं।
जीरो वेनवेल

9
इसका ऑटो बॉक्सिंग से कोई लेना-देना नहीं है, जो सिर्फ C # में होता है। अंतर यह है कि C # जावा (जबकि केवल 5 जावा!) के बाद से एक सामान्य सुपर-टाइप खोजने की कोशिश नहीं करता है। आप आसानी से यह देख सकते हैं कि यदि आप इसे 2 कस्टम वर्गों (जो दोनों वस्तु से स्पष्ट रूप से प्राप्त करते हैं) के साथ प्रयास करें तो यह देखने की कोशिश करें। इसके अलावा से एक अंतर्निहित रूपांतरण intहै object
वू

3
और nitpick करने के लिए सिर्फ एक थोड़ा और अधिक: से रूपांतरण Integer/Stringकरने के लिए Objectनहीं एक संकुचन रूपांतरण, यह ठीक विपरीत है है :-)
Voo

1
Integerऔर Stringभी लागू करते हैं Serializableऔर Comparableइसलिए उनमें से किसी के लिए असाइनमेंट भी काम करेंगे, उदाहरण के लिए Comparable<?> c=condition? 6: "6";या List<? extends Serializable> l = condition? new ArrayList<Integer>(): new ArrayList<String>();कानूनी जावा कोड।
होल्गर

5

यह बहुत सीधा है। स्ट्रिंग और इंट के बीच कोई अंतर्निहित रूपांतरण नहीं है। ternary ऑपरेटर को एक ही प्रकार के अंतिम दो ऑपरेंड की आवश्यकता होती है।

प्रयत्न:

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

11
सवाल यह है कि जावा और सी # के बीच अंतर का कारण क्या है। इस सवाल का जवाब नहीं है।
११:२१ बजे जीरो वेनवेल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.