2010-10-08 8 views
6

मेरे पास एक अधिभार विधि है - पहला कार्यान्वयन हमेशा एक ही वस्तु देता है, दूसरा कार्यान्वयन हमेशा एक गणना देता है।मैं जेनेरिक प्रतिबंधों का उपयोग करके एक विशिष्ट प्रकार को रोकता हूं

मैं और अतिभारित तरीकों सामान्य बनाने के लिए, और गैर गणन विधि से आबद्ध करने के लिए जब सामान्य प्रकार गणनीय है प्रयास कर से संकलक सीमित करना चाहते हैं ...

class Cache 
{ 
    T GetOrAdd<T> (string cachekey, Func<T> fnGetItem) 
     where T : {is not IEnumerable} 
    { 
    } 

    T[] GetOrAdd<T> (string cachekey, Func<IEnumerable<T>> fnGetItem) 
    { 
    } 
} 

होना करने के लिए साथ ... इस्तेमाल किया

{ 
    // The compile should choose the 1st overload 
    var customer = Cache.GetOrAdd("FirstCustomer",() => context.Customers.First()); 

    // The compile should choose the 2nd overload 
    var customers = Cache.GetOrAdd("AllCustomers",() => context.Customers.ToArray()); 
} 

यह है कि मैं यहाँ पर उल्लंघन कर रहा हूँ सादे बुरा कोड-गंध है या इसके बाद के तरीकों को स्पष्ट करने के लिए इतना है कि संकलक हमेशा बुला कोड सही मिल जाएगा संभव है?

किसी भी व्यक्ति के लिए ऊपर वोट जो "विधियों में से किसी एक का नाम बदलें" के अलावा कोई जवाब दे सकता है।

+0

मुझे आश्चर्य है कि अगर एक विस्तार विधि के रूप में कम से कम पसंदीदा लिखना नौकरी करेगा। Untested। –

+3

मामलों को और जटिल बनाने के लिए, ध्यान रखें कि आप शायद 'स्ट्रिंग' आइटम को उदाहरण के लिए बाहर नहीं करना चाहेंगे, लेकिन 'string' * *' IEnumerable' और 'IEnumerable ' लागू करें। – herzmeister

+0

मैंने अभी देखा है कि जब तक मुझे कुछ याद नहीं आ रहा है, तब तक इनमें से कोई भी घोषणा संकलित नहीं होगी, क्योंकि आप 'शून्य' * और * रिटर्न प्रकार कह रहे हैं। 'शून्य' के बिना, आप फिर रिटर्न प्रकार पर अधिभार करने का प्रयास करेंगे, जो जेनेरिक के किसी भी प्रश्न को छोड़कर नो-नो नहीं है। – AakashM

उत्तर

2

केवल एक विधि का उपयोग करें और इसे सामान्य बाधाओं के माध्यम से असंभव प्रयास करने के बजाय गतिशील रूप से IEnumerable<T> केस का पता लगाएं। यदि स्टोर को स्टोर/पुनर्प्राप्त करने के लिए ऑब्जेक्ट कुछ समझा जा सकता है या नहीं, तो यह दो अलग-अलग कैश विधियों से निपटने के लिए "कोड गंध" होगा। इसके अलावा, सिर्फ इसलिए कि यह IEnumerable<T> लागू करता है इसका मतलब यह नहीं है कि यह एक संग्रह है।

+0

मुझे लगता है कि आपने इसे काफी पसंद किया है। पोस्ट किए गए उत्तरों पर प्रतिबिंबित करने के बाद, मेरा मानना ​​है कि मैं जो प्रयास कर रहा था वह खराब कोड छोटा है ... यदि कॉलिंग कोड विधि/एस के विभिन्न कार्यों के लिए अनजान होना चाहिए, तो विधि हस्ताक्षर में कोई अंतर नहीं होना चाहिए ... यह है ... एक विधि होनी चाहिए, जैसा कि आपने बताया है, क्या यह आंतरिक रूप से जांच कर रहा है। धन्यवाद! – Mark

+0

@ मार्क - मुझे आशा है कि इससे मदद मिलेगी। बस आपको गलत समस्या को हल करने से रोकने की कोशिश कर रहा है :)। स्वीकृत उत्तर? –

1

बाधाएं बहिष्कार का समर्थन नहीं करती हैं, जो पहले निराशाजनक लगती है, लेकिन यह सुसंगत है और समझ में आता है (उदाहरण के लिए, इंटरफेस यह निर्धारित नहीं करता कि क्या नहीं कर सकता है)।

कहा जा रहा है, तो आप अपने IEnumerable अधिभार की कमी के साथ चारों ओर खेल सकते हैं ... हो सकता है आपके विधि दो सामान्य typings के लिए "where X : IEnumerable<T>" की तरह की कोई समस्या के साथ <X, T> बदलते हैं?

ईटीए निम्नलिखित कोड नमूना:

void T[] GetOrAdd<X,T> (string cachekey, Func<X> fnGetItem) 
      where X : IEnumerable<T> 
    { 
    } 
8

तरीकों में से एक का नाम बदलें। आप देखेंगे कि List<T> में एक जोड़ें और जोड़ें श्रेणी शामिल करें; उस पैटर्न का पालन करें। किसी आइटम को कुछ करना और वस्तुओं के अनुक्रम में कुछ करना तर्कसंगत रूप से अलग-अलग कार्य हैं, इसलिए विधियों के अलग-अलग नाम होते हैं।

+1

ऐसा प्रतीत होता है कि ओपी एक कैशिंग इंटरफ़ेस डिज़ाइन कर रहा है। पहले से ही खुद को लिखा है, मुझे विश्वास है कि कैश को परवाह नहीं करना चाहिए कि क्या संग्रहित करने का प्रयास करने वाला ऑब्जेक्ट संग्रह है या नहीं; यह क्रमबद्ध प्रक्रिया की एक कार्यान्वयन जानकारी है। मेरी राय में, केवल एक विधि को सभी जरूरतों को पूरा करना चाहिए। –

+1

ठीक है, मैं इसे स्वीकार करता हूं। मैंने लोल किया – MojoFilter

+0

@MojoFilter: आप किस पर LOL'd? –

5

सी # कंपाइलर ओवरलोड रिज़ॉल्यूशन कैसे करता है और यह कैसे तय करता है कि किस विधि को बाध्य करना है, इसका समर्थन करने के लिए यह एक कठिन उपयोग केस है।

पहला मुद्दा यह है कि एक विधि के 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() प्रकार को जानने के बिना यह संभव नहीं है। व्यक्तिगत रूप से मेरा मानना ​​है कि कोड जिसका अर्थात् वाक्यविन्यास से आसानी से अनुमानित नहीं किया जा सकता है, जब संभव हो तो इससे बचा जाना चाहिए। हम बेहतर नाम चुनते हैं, तो इस समस्या को कम किया जाता है:

  1. बदलें तरीकों में से एक का नाम:

    Cache.Add("abc",() => context.Customers.Frobble()); 
    Cache.AddRange("xyz",() => context.Customers.Frobble()); 
    

    अंत में, केवल तीन विकल्प आपके उदाहरण में तरीकों को स्पष्ट करने के लिए कर रहे हैं ।

  2. जहां भी आप दूसरे अधिभार को कॉल करते हैं IEnumerable<T> पर कास्ट करें।
  3. संकलक को अलग-अलग तरीके से एक तरीके से हस्ताक्षर बदलें।

विकल्प 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()); 

तो संकलक मदद करने के लिए के लिए एक अधिभार चयन करने के लिए संकेत का उपयोग कर सकते हमें ... यकीन है। लेकिन डेवलपर के रूप में हमें वहां आने वाले सभी अतिरिक्त कार्यों को देखें (गलतियों के लिए पेश किए गए कुरूपता और अवसर का उल्लेख न करें)। क्या यह वास्तव में प्रयास के लायक है? विशेष रूप से जब एक आसान और भरोसेमंद तकनीक (विधियों को अलग-अलग नामकरण) हमारी मदद करने के लिए पहले से मौजूद है?

+0

एक शानदार, अच्छी तरह से समझाया गया जवाब ... धन्यवाद! – Mark

संबंधित मुद्दे

 संबंधित मुद्दे