2011-03-05 9 views
8

अतिभारित Enumerable.Select विधि के लिए निम्न कॉल:अधिभारित विधि-समूह तर्क ओवरलोड रिज़ॉल्यूशन को भ्रमित करता है?

var itemOnlyOneTuples = "test".Select<char, Tuple<char>>(Tuple.Create); 

एक अस्पष्टता त्रुटि (स्पष्टता के लिए हटा दिया नामस्थान) के साथ विफल रहता है:

The call is ambiguous between the following methods or properties: 
'Enumerable.Select<char,Tuple<char>> 
      (IEnumerable<char>,Func<char,Tuple<char>>)' 
and 
'Enumerable.Select<char,Tuple<char>> 
      (IEnumerable<char>, Func<char,int,Tuple<char>>)' 

मैं निश्चित रूप से क्यों नहीं समझ सकता हूँ प्रकार तर्क को निर्दिष्ट स्पष्ट रूप से एक अस्पष्टता के परिणामस्वरूप (ओवरलोड दोनों लागू होंगे), लेकिन ऐसा करने के बाद मुझे कोई नहीं दिखाई देता है।

यह मेरे लिए पर्याप्त स्पष्ट प्रतीत होता है कि इरादा पहले ओवरलोड को कॉल करना है, विधि-समूह तर्क Tuple.Create<char>(char) पर हल हो रहा है। दूसरा अधिभार लागू नहीं होना चाहिए क्योंकि Tuple.Create ओवरलोड में से कोई भी अपेक्षित Func<char,int,Tuple<char>> प्रकार में परिवर्तित नहीं किया जा सकता है। मैं अनुमान संकलक Tuple.Create<char, int>(char, int) द्वारा भ्रमित है, लेकिन इसका रिटर्न-प्रकार गलत है: यह दो-टुपल देता है, और इसलिए प्रासंगिक Func प्रकार में परिवर्तनीय नहीं है।

  1. विधि-समूह तर्क के लिए एक प्रकार का तर्क निर्दिष्ट करना:: Tuple.Create<char> (शायद यह वास्तव में एक प्रकार निष्कर्ष मुद्दा है?)

    वैसे, निम्न में से कोई संकलक खुश करता।

  2. तर्क को विधि-समूह के बजाय लैम्बडा-अभिव्यक्ति बनाना: x => Tuple.Create(x)। (Select कॉल पर टाइप-इनफरेंस के साथ अच्छी तरह से खेलता है)।

हैरानगी, इस तरह से Select के अन्य अधिभार कॉल करने का प्रयास भी विफल रहता है:

var itemIndexTwoTuples = "test".Select<char, Tuple<char, int>>(Tuple.Create); 

क्या सटीक समस्या यहाँ है?

उत्तर

20

सबसे पहले, मैं ध्यान दें कि इस का डुप्लिकेट है:

Why is Func<T> ambiguous with Func<IEnumerable<T>>?

क्या सटीक समस्या यहाँ है?

थॉमस का अनुमान अनिवार्य रूप से सही है। यहां सटीक विवरण दिए गए हैं।

चलिए एक समय में एक कदम से गुजरते हैं।हमारे पास एक आमंत्रण है:

"test".Select<char, Tuple<char>>(Tuple.Create); 

ओवरलोड रिज़ॉल्यूशन को चुनने के लिए कॉल का अर्थ निर्धारित करना होगा। स्ट्रिंग या स्ट्रिंग के किसी भी बेस क्लास पर "चयन करें" कोई विधि नहीं है, इसलिए यह एक विस्तार विधि होनी चाहिए।

उम्मीदवार सेट के लिए कई संभावित विस्तार विधियां हैं क्योंकि स्ट्रिंग परिवर्तनीय है IEnumerable<char> और संभवतः वहां कहीं using System.Linq; है। पैटर्न से मेल खाने वाले कई एक्सटेंशन विधियां हैं "चयन, जेनेरिक आर्टी दो, IEnumerable<char> को दिए गए विधि प्रकार तर्कों के साथ बनाए गए पहले तर्क के रूप में लेते हैं"।

विशेष रूप से, उम्मीदवारों में से दो हैं:

Enumerable.Select<char,Tuple<char>>(IEnumerable<char>,Func<char,Tuple<char>>) 
Enumerable.Select<char,Tuple<char>>(IEnumerable<char>,Func<char,int,Tuple<char>>) 

अब, पहला सवाल का हम सामना है उम्मीदवारों लागू कर रहे हैं? यही है, क्या प्रत्येक आपूर्ति किए गए तर्क से संबंधित औपचारिक पैरामीटर प्रकार में कोई अंतर्निहित रूपांतरण है?

एक उत्कृष्ट सवाल। स्पष्ट रूप से पहला तर्क "रिसीवर" होगा, एक स्ट्रिंग, और यह IEnumerable<char> पर स्पष्ट रूप से परिवर्तनीय होगा। सवाल यह है कि दूसरा तर्क, विधि समूह "टुपल। क्रेट", औपचारिक रूप से औपचारिक पैरामीटर प्रकार Func<char,Tuple<char>>, और Func<char,int, Tuple<char>> में परिवर्तनीय है।

किसी दिए गए प्रतिनिधि प्रकार में परिवर्तनीय विधि समूह कब होता है? एक विधि समूह एक प्रतिनिधि प्रकार के लिए परिवर्तनीय है जब ओवरलोड रिज़ॉल्यूशन उसी प्रकार के तर्क दिए गए होंगे जैसे प्रतिनिधि के औपचारिक पैरामीटर प्रकार

यही है, एम Func<A, R> में परिवर्तनीय है यदि फॉर्म M(someA) के कॉल पर ओवरलोड रिज़ॉल्यूशन 'ए' प्रकार 'ए' अभिव्यक्ति के बाद सफल हो गया होगा।

क्या ओवरलोड रिज़ॉल्यूशन Tuple.Create(someChar) पर कॉल पर सफल हो गया है? हाँ; अधिभार संकल्प Tuple.Create<char>(char) चुना होगा।

क्या ओवरलोड रिज़ॉल्यूशन Tuple.Create(someChar, someInt) पर कॉल पर सफल हुआ होगा? हां, ओवरलोड रिज़ॉल्यूशन Tuple.Create<char,int>(char, int) चुना होगा।

चूंकि दोनों मामलों में ओवरलोड रिज़ॉल्यूशन सफल हो गया होगा, विधि समूह दोनों प्रतिनिधि प्रकारों में परिवर्तनीय है। तथ्य यह है कि विधियों में से किसी एक का रिटर्न प्रकार मेल नहीं खाता होगा, प्रतिनिधि का रिटर्न प्रकार अप्रासंगिक है; अधिभार संकल्प सफल प्रकार विश्लेषण के आधार पर सफल या असफल नहीं होता है।

कोई उचित रूप से कह सकता है कि विधि समूहों से परिवर्तनीयता प्रकार को वापसी प्रकार विश्लेषण के आधार पर सफल या असफल होना चाहिए, लेकिन ऐसा नहीं है कि भाषा निर्दिष्ट कैसे है; भाषा समूह रूपांतरण के परीक्षण के रूप में अधिभार संकल्प का उपयोग करने के लिए भाषा निर्दिष्ट है, और मुझे लगता है कि यह एक उचित विकल्प है।

इसलिए हमारे पास दो लागू उम्मीदवार हैं। क्या कोई तरीका है कि हम तय कर सकते हैं कि अन्य 0 की तुलना में कौन सा है?Spec का कहना है कि में रूपांतरण अधिक विशिष्ट प्रकार बेहतर है; यदि आपके पास

void M(string s) {} 
void M(object o) {} 
... 
M(null); 

तो ओवरलोड रिज़ॉल्यूशन स्ट्रिंग संस्करण चुनता है क्योंकि स्ट्रिंग ऑब्जेक्ट से अधिक विशिष्ट है। क्या उन प्रतिनिधि प्रकारों में से एक दूसरे की तुलना में अधिक विशिष्ट है? नहीं। न तो दूसरे की तुलना में अधिक विशिष्ट है। (यह बेहतर रूपांतरण नियमों का एक सरलीकरण है; वास्तव में बहुत सारे टाईब्रेकर्स हैं, लेकिन उनमें से कोई भी यहां लागू नहीं होता है।)

इसलिए दूसरे पर एक को प्राथमिकता देने का कोई आधार नहीं है।

फिर से, कोई उचित रूप से कह सकता है कि, एक आधार है, अर्थात्, उन रूपांतरणों में से एक प्रतिनिधि प्रतिनिधि रिटर्न प्रकार मेलबैक त्रुटि उत्पन्न करेगा और उनमें से एक नहीं होगा। फिर भी, भाषा औपचारिक पैरामीटर प्रकार के बीच संबंधों पर विचार करके, और आपके द्वारा चुने गए रूपांतरण के परिणामस्वरूप अंततः एक त्रुटि के परिणामस्वरूप सख्तता के कारण के लिए निर्दिष्ट किया गया है।

चूंकि कोई आधार नहीं है जिस पर एक दूसरे को पसंद करना है, यह एक अस्पष्टता त्रुटि है।

समान अस्पष्टता त्रुटियों को बनाना आसान है। उदाहरण के लिए:

void M(Func<int, int> f){} 
void M(Expression<Func<int, int>> ex) {} 
... 
M(x=>Q(++x)); 

यह संदिग्ध है। भले ही अभिव्यक्ति वृक्ष के अंदर ++ होना अवैध है, परिवर्तनीय तर्क इस बात पर विचार नहीं करता है कि लैम्ब्डा के शरीर में कुछ ऐसा है जो एक अभिव्यक्ति वृक्ष में अवैध होगा। रूपांतरण तर्क केवल यह सुनिश्चित करता है कि प्रकार जांचें, और वे करते हैं। यह देखते हुए कि एम में से किसी एक को पसंद करने का कोई कारण नहीं है, इसलिए यह एक अस्पष्टता है।

आप ध्यान दें कि

"test".Select<char, Tuple<char>>(Tuple.Create<char>); 

सफल होता है। अब आप जानते हैं क्यों। अधिभार संकल्प का निर्धारण करना चाहिए अगर

Tuple.Create<char>(someChar) 

या

Tuple.Create<char>(someChar, someInt) 

कामयाब होने की। चूंकि पहला व्यक्ति करता है और दूसरा नहीं करता है, दूसरा उम्मीदवार अपरिवर्तनीय और समाप्त हो जाता है, और इसलिए अस्पष्ट बनने के लिए नहीं है।

तुम भी ध्यान दें कि

"test".Select<char, Tuple<char>>(x=>Tuple.Create(x)); 

स्पष्ट है। लैम्ब्डा रूपांतरण खाते के प्रतिनिधि के रिटर्न प्रकार के साथ लौटाई गई अभिव्यक्ति के प्रकार की संगतता को ध्यान में रखें। यह दुर्भाग्यपूर्ण है कि विधि समूह और लैम्ब्डा अभिव्यक्ति परिवर्तनीयता निर्धारित करने के लिए दो सूक्ष्म रूप से अलग-अलग एल्गोरिदम का उपयोग करती हैं, लेकिन हम अब इसके साथ अटक गए हैं। याद रखें, विधि समूह रूपांतरण भाषा में लैम्ब्डा रूपांतरणों से काफी लंबा है; क्या उन्हें एक ही समय में जोड़ा गया था, मुझे लगता है कि उनके नियमों को सुसंगत बना दिया गया होगा।

+4

एरिक, यह बयान है कि * "हम [दो सूक्ष्म रूप से अलग एल्गोरिदम] * * के साथ फंस गए हैं, यह बताता है कि वहां एक है यहां खेलने पर पिछड़ा संगतता विचार, हां? संभावित रूप से संकल्प के लिए रिटर्न प्रकार विश्लेषण का उपयोग करने के लिए विधि समूह रूपांतरणों को अनुमति देने से ऐसी स्थितियां पैदा होंगी जहां मौजूदा कोड के परिणामस्वरूप एक अलग ओवरलोड विकल्प होगा। हालांकि, मैं एक परिदृश्य नहीं बना सकता जहां यह वास्तव में होगा - वास्तव में ऐसा कोई मामला है? या क्या यह कम मूल्य वाले उपयोग के मामले में जोखिम/इनाम को संतुलित करने का मामला है? – LBushkin

+0

मैं वास्तव में इस उत्तर में विस्तार की सराहना करता हूं; मुझे लगता है कि अब मुझे थोड़ा बेहतर समझ है। मैं वास्तव में अपने कोड को काम करने के लिए आधा उम्मीद करता था क्योंकि मैं इस धारणा के तहत था कि विधि-समूहों के रिटर्न-प्रकार को सी # 4 में अधिक 'ध्यान' दिया गया था, लेकिन संभवतः यह केवल प्रकार-अनुमान के लिए है? वैसे भी, मेरे लिए, ओवरलोड रिज़ॉल्यूशन और टाइप-इनफरेंस में उनके बारे में थोड़ा काला जादू है; मेरा अंतर्ज्ञान मुझे अक्सर नीचे जाने देता है। – Ani

+0

एक और नोट पर, मुझे थोड़ा परेशान लगता है कि सी # लगातार निम्नलिखित दो पहलुओं को इतनी दृढ़ता से अलग करता है: ए) उपयोगकर्ता का क्या मतलब हो सकता है बी) वह चीज़ है जिसका उपयोगकर्ता कानूनी हो सकता है। क्या 'बी)' में एक बड़ी भूमिका निभाना गलत है? – Ani

5

मुझे लगता है कि संकलक Tuple.Create<char, int>(char, int) द्वारा उलझन में है, लेकिन इसका रिटर्न-प्रकार गलत है: यह दो-टुपल देता है।

वापसी का प्रकार विधि हस्ताक्षर का हिस्सा नहीं है, इसलिए इसे ओवरलोड रिज़ॉल्यूशन के दौरान नहीं माना जाता है; यह केवल के बाद सत्यापित किया गया है एक अधिभार चुना गया है। तो जहां तक ​​संकलक जानता है, Tuple.Create<char, int>(char, int) एक वैध उम्मीदवार है, और यह न तो बेहतर है और न ही Tuple.Create<char>(char) से भी बदतर है, इसलिए संकलक निर्णय नहीं ले सकता है।

+0

धन्यवाद, यह बहुत व्यावहारिक लगता है। क्या आपके पास इसकी पुष्टि करने के लिए कोई संदर्भ है? – Ani

+0

यह सिर्फ व्यवहार्य नहीं है, यह सटीक है। एसओ में संदर्भ मुश्किल हैं, वोट अज्ञात हैं। –

+0

@ हंस: क्षमा करें, आपकी टिप्पणी को समझ में नहीं आया। मैं दस्तावेज के बारे में बात कर रहा था, अगर यह स्पष्ट नहीं था। अापका नजरिया क्या है? – Ani