सी # कंपाइलर ओवरलोड रिज़ॉल्यूशन कैसे करता है और यह कैसे तय करता है कि किस विधि को बाध्य करना है, इसका समर्थन करने के लिए यह एक कठिन उपयोग केस है।
पहला मुद्दा यह है कि एक विधि के constraints are not part of the signature और ओवरलोड रिज़ॉल्यूशन के लिए विचार नहीं किया जाएगा।
दूसरी समस्या जो आपको दूर करने के लिए मिल गई है वह यह है कि संकलक उपलब्ध हस्ताक्षर से सबसे अच्छा मिलान चुनता है - जो, जेनेरिक से निपटने पर आम तौर पर इसका मतलब है कि SomeMethod<T>(T)
SomeMethod<T>(IEnumerable<T>)
से बेहतर मिलान माना जाएगा ... विशेष रूप से जब आपके पास T[]
या List<T>
जैसे पैरामीटर हैं।
लेकिन अधिक मौलिक रूप से, आपको यह विचार करना होगा कि मूल्यों का संग्रह बनाम एक मूल्य पर ऑपरेटिंग वास्तव में समान ऑपरेशन है। यदि वे तार्किक रूप से भिन्न हैं, तो आप शायद स्पष्टता के लिए अलग-अलग नामों का उपयोग करना चाहते हैं।शायद कुछ उपयोग के मामले हैं जहां आप तर्क दे सकते हैं कि वस्तुओं के संग्रह और वस्तुओं के संग्रह के बीच अर्थपूर्ण अंतर सार्थक नहीं हैं ... लेकिन उस मामले में, दो अलग-अलग तरीकों को क्यों लागू करें? यह अस्पष्ट है कि मतभेदों को व्यक्त करने का सबसे अच्छा तरीका विधि अधिभार है। एक उदाहरण है कि भ्रम को उधार देता है पर नजर डालते हैं:
Cache.GetOrAdd("abc",() => context.Customers.Frobble());
सबसे पहले, ध्यान दें कि उदाहरण में ऊपर हम वापसी पैरामीटर अनदेखी करने के लिए चुन रहे हैं। दूसरा, ध्यान दें कि हम Customers
संग्रह पर कुछ विधि Frobble()
पर कॉल करते हैं। अब आप मुझे बता सकते हैं कि GetOrAdd()
का अधिभार किससे बुलाया जाएगा? स्पष्ट रूप से Frobble()
प्रकार को जानने के बिना यह संभव नहीं है। व्यक्तिगत रूप से मेरा मानना है कि कोड जिसका अर्थात् वाक्यविन्यास से आसानी से अनुमानित नहीं किया जा सकता है, जब संभव हो तो इससे बचा जाना चाहिए। हम बेहतर नाम चुनते हैं, तो इस समस्या को कम किया जाता है:
- बदलें तरीकों में से एक का नाम:
Cache.Add("abc",() => context.Customers.Frobble());
Cache.AddRange("xyz",() => context.Customers.Frobble());
अंत में, केवल तीन विकल्प आपके उदाहरण में तरीकों को स्पष्ट करने के लिए कर रहे हैं ।
- जहां भी आप दूसरे अधिभार को कॉल करते हैं
IEnumerable<T>
पर कास्ट करें। - संकलक को अलग-अलग तरीके से एक तरीके से हस्ताक्षर बदलें।
विकल्प 1 स्वयं स्पष्ट है, इसलिए मैं इसके बारे में और कुछ नहीं कहूंगा।
विकल्प 2 भी समझने में आसान है:
var customers = Cache.GetOrAdd("All",
() => (IEnumerable<Customer>)context.Customers.ToArray());
विकल्प 3 अधिक जटिल है। आइए देखें कि हम इसे कैसे प्राप्त कर सकते हैं।
दृष्टिकोण पर उदाहरण के लिए, Func<>
प्रतिनिधि के हस्ताक्षर बदलकर है:
T GetOrAdd<T> (string cachekey, Func<object,T> fnGetItem)
T[] GetOrAdd<T> (string cachekey, Func<IEnumerable<T>> fnGetItem)
// now we can do:
var customer = Cache.GetOrAdd("First", _ => context.Customers.First());
var customers = Cache.GetOrAdd("All",() => context.Customers.ToArray());
व्यक्तिगत रूप से, मैं इस विकल्प बहुत बदसूरत unintuitive, और भ्रामक हैं। एक अप्रयुक्त पैरामीटर पेश करना भयानक है ... लेकिन, दुख की बात है कि यह काम करेगा।
हस्ताक्षर (जो कुछ हद तक कम भयानक है) बदलने का एक वैकल्पिक तरीका वापसी मान एक out
पैरामीटर बनाना है:
void GetOrAdd<T> (string cachekey, Func<object,T> fnGetItem, out T);
void GetOrAdd<T> (string cachekey, Func<IEnumerable<T>> fnGetItem, out T[])
// now we can write:
Customer customer;
Cache.GetOrAdd("First", _ => context.Customers.First(), out customer);
Customer[] customers;
var customers = Cache.GetOrAdd("All",
() => context.Customers.ToArray(), out customers);
लेकिन यह वास्तव में बेहतर है? यह हमें इन विधियों का उपयोग अन्य विधि कॉल के पैरामीटर के रूप में करने से रोकता है। यह कोड को कम स्पष्ट और कम समझने योग्य बनाता है, आईएमओ।
एक अंतिम विकल्प मैं पेश करेंगे तरीकों जो वापसी मान प्रकार को पहचानने के लिए एक और सामान्य पैरामीटर जोड़ने के लिए है:
T GetOrAdd<T> (string cachekey, Func<T> fnGetItem);
R[] GetOrAdd<T,R> (string cachekey, Func<IEnumerable<T>> fnGetItem);
// now we can do:
var customer = Cache.GetOrAdd("First", _ => context.Customers.First());
var customers = Cache.GetOrAdd<Customer,Customer>("All",() => context.Customers.ToArray());
तो संकलक मदद करने के लिए के लिए एक अधिभार चयन करने के लिए संकेत का उपयोग कर सकते हमें ... यकीन है। लेकिन डेवलपर के रूप में हमें वहां आने वाले सभी अतिरिक्त कार्यों को देखें (गलतियों के लिए पेश किए गए कुरूपता और अवसर का उल्लेख न करें)। क्या यह वास्तव में प्रयास के लायक है? विशेष रूप से जब एक आसान और भरोसेमंद तकनीक (विधियों को अलग-अलग नामकरण) हमारी मदद करने के लिए पहले से मौजूद है?
मुझे आश्चर्य है कि अगर एक विस्तार विधि के रूप में कम से कम पसंदीदा लिखना नौकरी करेगा। Untested। –
मामलों को और जटिल बनाने के लिए, ध्यान रखें कि आप शायद 'स्ट्रिंग' आइटम को उदाहरण के लिए बाहर नहीं करना चाहेंगे, लेकिन 'string' * *' IEnumerable' और 'IEnumerable' लागू करें। –
herzmeister
मैंने अभी देखा है कि जब तक मुझे कुछ याद नहीं आ रहा है, तब तक इनमें से कोई भी घोषणा संकलित नहीं होगी, क्योंकि आप 'शून्य' * और * रिटर्न प्रकार कह रहे हैं। 'शून्य' के बिना, आप फिर रिटर्न प्रकार पर अधिभार करने का प्रयास करेंगे, जो जेनेरिक के किसी भी प्रश्न को छोड़कर नो-नो नहीं है। – AakashM