2012-07-19 3 views
20

में वैरिएबल जेनेरिक रिटर्न टाइप क्या कोई विधि किसी विधि से कई सामान्य प्रकारों में से किसी एक को वापस करने का कोई तरीका है? उदाहरण के लिए, मैं निम्नलिखित है:सी #

public static T ParseAttributeValue<T>(this XElement element, string attribute) 
    { 
     if(typeof(T) == typeof(Int32)) 
     { 
      return Int32.Parse(element.Attribute(attribute).Value); 
     } 

     if(typeof(T) == typeof(Double)) 
     { 
      return Double.Parse(element.Attribute(attribute).Value); 
     } 

     if(typeof(T) == typeof(String)) 
     { 
      return element.Attribute(attribute).Value; 
     } 

     if(typeof(T) == typeof(ItemLookupType)) 
     { 
      return Enum.Parse(typeof(T), element.Attribute(attribute).Value); 
     } 
    } 

(यह, केवल एक बहुत त्वरित mockup है मुझे पता है कि किसी भी उत्पादन कोड अशक्त चेकों आदि में काफी अधिक गहन करने की आवश्यकता होगी हूँ ...)

लेकिन संकलक इसे पसंद नहीं करते हैं, शिकायत करते हैं कि Int32 को स्पष्ट रूप से T में परिवर्तित नहीं किया जा सकता है (यह किसी भी कलाकार के साथ काम नहीं करता है)। मैं समझ सकता हूँ। संकलन समय पर यह जानने का कोई तरीका नहीं है कि T क्या है, लेकिन मैं इसे पहले से जांच रहा हूं। क्या वैसे भी मैं यह काम कर सकता हूं?

+0

नहीं, ' 'केवल टाइप पैरामीटर है। आप ब्रैकेट के बिना प्रकार का उपयोग करते हैं। – Femaref

+0

@ फेमेरेफ - अच्छा बिंदु। प्रश्न वापस ले लिया गया। –

+0

यदि आप ऑब्जेक्ट को ऑब्जेक्ट के रूप में संग्रहीत करते हैं, तो आप इसे कास्ट कर सकते हैं। यह भी संभव है कि फ़ंक्शन को गतिशील के रूप में टाइप करने से आप बिना कलाकार के ऐसा करने में मदद कर सकते हैं। – MrWednesday

उत्तर

20

मैंने अतीत में इन प्रकार के सामान्य तरीकों को किया है। टाइप अनुमान प्राप्त करने का सबसे आसान तरीका एक सामान्य कनवर्टर फ़ंक्शन प्रदान करना है।

public static T ParseAttributeValue<T> 
      (this XElement element, string attribute, Func<string, T> converter) 
{ 
    string value = element.Attribute(attribute).Value; 
    if (String.IsNullOrWhiteSpace(value)) { 
    return default(T); 
    } 

    return converter(value); 
} 

आपको निम्न रूप में इसका इस्तेमाल कर सकते हैं:

int index = element.ParseAttributeValue("index", Convert.ToInt32); 
double price = element.ParseAttributeValue("price", Convert.ToDouble); 

आप अपने स्वयं के कार्यों प्रदान करते हैं और दुनिया में सभी मजा कर सकते हैं (यहां तक ​​कि गुमनाम प्रकार वापसी):

ItemLookupType lookupType = element.ParseAttributeValue("lookupType", 
    value => Enum.Parse(typeof(ItemLookupType), value)); 

var item = element.ParseAttributeValue("items", 
    value => { 
    List<string> items = new List<string>(); 
    items.AddRange(value.Split(new [] { ',' })); 
    return items; 
    }); 
+0

मुझे नहीं लगता कि यह उससे कहीं ज्यादा सुरुचिपूर्ण हो सकता है। +1। – Adam

+0

इस बारे में अच्छी बात यह है कि 'टी' को उपयोग से अनुमानित किया जा सकता है क्योंकि यह 'Func <,>' के अंदर है। तो अगर हमें अतिरिक्त पैरामीटर (अर्थात् 'Func <,>' प्रतिनिधि) प्रदान करना है, तो हमें स्पष्ट रूप से टाइप पैरामीटर 'टी' निर्दिष्ट नहीं करना होगा। –

+1

नेट फ्रेमवर्क में बहुत से जेनेरिक कन्वर्टर्स पहले से मौजूद हैं: 'TypeDescriptor.GetTypeConverter (typeof (T));' –

4

आप प्रकार पैरामीटर का उपयोग रिटर्न प्रकार के रूप में क्यों कर रहे हैं? यह काम करेगा, बस एक डाली आवश्यकता बुला के बाद:

public static Object ParseAttributeValue<T>(this XElement element, string attribute) 
{ 
    if(typeof(T) == typeof(Int32)) 
    { 
     return Int32.Parse(element.Attribute(attribute).Value); 
    } 

    if(typeof(T) == typeof(Double)) 
    { 
     return Double.Parse(element.Attribute(attribute).Value); 
    } 

    if(typeof(T) == typeof(String)) 
    { 
     return element.Attribute(attribute).Value; 
    } 

    if(typeof(T) == typeof(ItemLookupType)) 
    { 
     return Enum.Parse(typeof(T), element.Attribute(attribute).Value); 
    } 
} 

या बेहतर अभी तक:

public static Int32 ParseAsInt32(this XElement element, string attribute) 
{ 
    return Int32.Parse(element.Attribute(attribute).Value); 
} 

// etc, repeat for each type 

यह दूसरा दृष्टिकोण inlined हो रही का एक बहुत उच्च संभावना होने का अतिरिक्त लाभ है, के साथ साथ यह होगा (Int32 जैसे मान प्रकारों के लिए) मूल्य/बॉक्स को अनबॉक्स करने की आवश्यकता को रोकें। इनमें से दोनों कुछ हद तक तेज करने की विधि का कारण बनेंगे।

+0

+1। इस प्रकार को समझने के लिए प्रतिबिंब का उपयोग करने का कोई कारण नहीं है और जब भाषा पहले से अधिभारित विधियां प्रदान करती है, तो एक ऐसी सुविधा जो आपके लिए यह काम करती है (और शायद अधिक कुशलता से)। –

+0

हां, जेनेरिकों का त्याग करना और विशिष्ट रूप से नामित विधियां बनाना इसका एक आम समाधान है, न कि बुरा। 'ParseAttributeValueAsString' टाइपिंग' ParseAttributeValue 'के रूप में लगभग इंटेलिजेंस के साथ या बिना आसान है। और सामान्य विधि पहले से ही विशेष मामलों का एक समूह था, इसलिए यदि आप इसे अलग-अलग तरीकों से खींचते हैं तो कोई डुप्लिकेशन नहीं होता है। –

2

यह सुनिश्चित नहीं है कि यह वही है जो आप चाहते हैं, लेकिन यदि आप object पर पहले T

पर लौटते हैं तो आप रिटर्न काम कर सकते हैं
public static T ParseAttributeValue<T>(this XElement element, string attribute) 
    { 
     if (typeof(T) == typeof(Int32)) 
     { 
      return (T)(object)Int32.Parse(element.Attribute(attribute).Value); 
     } 

     if (typeof(T) == typeof(Double)) 
     { 
      return (T)(object)Double.Parse(element.Attribute(attribute).Value); 
     } 

     if (typeof(T) == typeof(String)) 
     { 
      return (T)(object)element.Attribute(attribute).Value; 
     } 

     return default(T); 
    } 

आप अभी भी संकलन समय पर T प्रदान करने के लिए हालांकि, की तरह विधि बुला:

int value = element.ParseAttributeValue<int>("attribute"); 
+0

यह तत्काल समाधान है। इसे हमेशा 'ऑब्जेक्ट' पर टाइप पैरामीटर को स्पष्ट रूप से डालने की अनुमति दी जाती है, और तब 'ऑब्जेक्ट' को किसी भी चीज़ पर डाला जा सकता है।लेकिन यह _is_ बदसूरत है। और इसमें 'Int32',' Double', और अन्य structs के मामले में मुक्केबाजी और अनबॉक्सिंग शामिल है। –

2

यहाँ यह करने के दो तरीके है ...

static T ReadSetting<T>(string value) 
    { 
     object valueObj = null; 
     if (typeof(T) == typeof(Int32)) 
      valueObj = Int32.Parse(value); 
     return (T)valueObj; 
    } 
    static dynamic ReadSetting2<T>(string value) 
    { 
     if (typeof(T) == typeof(Int32)) 
      return Int32.Parse(value); 
     throw new UnsupportedException("Type is unsupported"); 
    } 
    static void Main(string[] args) 
    { 
     int val1 = ReadSetting<Int32>("2"); 
     int val2 = ReadSetting2<Int32>("3"); 
    } 
+1

'रीडसेटिंग' एक सामान्य प्रकार एक मान प्रकार है, क्योंकि मान प्रकार शून्य नहीं हो सकता है, इसलिए 'NullReferenceException' फेंक देगा। यही कारण है कि 'ऑब्जेक्ट' के लिए बॉक्स के लिए खतरनाक है। – Joshua

+0

यह सच है, लेकिन यह कई मामलों में उचित परिणाम हो सकता है। उदाहरण के लिए, यह दिखाते हुए कि यह पद्धतियों को दिखाने के लिए लिखा गया सिर्फ सरल नमूना कोड नहीं था, एक कॉलर उचित रूप से एक अपवाद की उम्मीद कर सकता है अगर वे उस सेटिंग को पढ़ने की कोशिश कर रहे हैं जिसे पार्स नहीं किया जा सकता था या निर्दिष्ट नहीं किया गया था। – MrWednesday

1
सी के साथ

++ टेम्पलेट्स, इस चीज की तरह काम करेगा, लेकिन केवल अगर कोड का प्रत्येक टुकड़ा एक अलग, अलग विशेषज्ञता में था। वह काम जो उस काम को बनाता है वह है कि अप्रयुक्त फ़ंक्शन टेम्पलेट संकलित नहीं किए जाते हैं (या अधिक सटीक रूप से: पूरी तरह से तत्काल नहीं), इसलिए तथ्य यह है कि कोड का एक टुकड़ा अमान्य होगा यदि टेम्पलेट की उस प्रति को एक अलग प्रकार से तत्काल किया गया हो, ऊपर आओ।

सी # अलग है, और AFAIK जेनेरिक के लिए कोई विशेषज्ञता नहीं है। आप जो करने की कोशिश कर रहे हैं उसे पूरा करने का एक तरीका, सी # की सीमाओं के भीतर काम करते समय एक समारोह को अधिक अमूर्त रिटर्न प्रकार के साथ बनाना होगा, और केवल Par.AttributeValue का उपयोग इसे टी में डालने के लिए करना होगा।

तो तुम होगा:

private static Object AbstractParseValue(System.Type t, XElement element, string attribute) 

और

public static T ParseAttributeValue<T>(this XElement element, string attribute) 
{ 
    return (T)AbstractParseValue(typeof(T), element, attribute); 
} 
8

नेट पहले से ही महान स्ट्रिंग रूपांतरण दिनचर्या आप उपयोग कर सकते हैं का एक समूह है! TypeConverter आपके लिए भारी भारोत्तोलन कर सकता है। फिर आपको अंतर्निहित प्रकारों के लिए अपने स्वयं के पार्सिंग कार्यान्वयन प्रदान करने की चिंता करने की आवश्यकता नहीं है।

ध्यान दें कि TypeConverter पर एपीआई के लोकेल-जागरूक संस्करण हैं जिनका उपयोग विभिन्न संस्कृतियों में व्यक्त पार्सिंग मानों को संभालने की आवश्यकता होने पर किया जा सकता है।

using System.ComponentModel; 

public static T ParseAttributeValue<T>(this XElement element, string attribute) 
{ 
    var converter = TypeDescriptor.GetConverter(typeof(T)); 
    if (converter.CanConvertFrom(typeof(string))) 
    { 
     string value = element.Attribute(attribute).Value; 
     return (T)converter.ConvertFromString(value); 
    } 

    return default(T); 
} 

यह एक TypeConverterAttribute साथ निर्मित प्रकार, और आप को सजाने कर सकते हैं कस्टम प्रकार का एक बहुत के लिए काम करेंगे उन्हें में भाग लेने के अनुमति देने के लिए:

निम्नलिखित कोड डिफ़ॉल्ट संस्कृति का उपयोग कर मूल्यों को पार्स जाएगा रूपांतरण खेल भी टाइप करें। इसका मतलब है कि भविष्य में आप ParseAttributeValue के कार्यान्वयन को बदलने के बिना नए प्रकारों को पार्स करने में सक्षम होंगे।

देखें: http://msdn.microsoft.com/en-us/library/system.componentmodel.typeconverter.aspx

0

मेरा सुझाव है कि बजाय प्रकार पैरामीटर हर बार नियमित क्रियान्वित किया जाता है परीक्षण की तुलना में, अगर आप इस तरह एक सामान्य स्थिर वर्ग कुछ बनाना चाहिए होगा:

 
internal static class ElementParser<T> 
{ 
    public static Func<XElement, string, T> Convert = InitConvert; 

    T DefaultConvert(XElement element, string attribute) 
    { 
    return Default(T); // Or maybe throw exception, or whatever 
    } 

    T InitConvert(XElement element, string attribute) 
    { 
    if (ElementParser<int>.Convert == ElementParser<int>.InitConvert) 
    { // First time here for any type at all 
     Convert = DefaultConvert; // May overwrite this assignment below 
     ElementParser<int>.Convert = 
     (XElement element, string attribute) => 
      Int32.Parse(element.Attribute(attribute).Value); 
     ElementParser<double>.Convert = 
     (XElement element, string attribute) => 
      Int32.Parse(element.Attribute(attribute).Value); 
     // etc. for other types 
    } 
    else // We've done other types, but not this type, and we don't do anything nice for it 
    { 
     Convert = DefaultConvert; 
    } 
    return Convert(element, attribute);  
    } 
} 
public static T ParseAttributeValue(this XElement element, string attribute) 
{ 
    ElementParser<T>.Convert(element, attribute); 
} 

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

+0

क्यों न केवल 'टाइप कनवर्टर' कक्षा में निर्मित का उपयोग करें? –

+0

यदि वह विशेष वर्ग किसी के आवेदन के लिए पर्याप्त रूप से काम करेगी, तो बढ़िया। अगर किसी को किसी भी विशेष व्यवहार की आवश्यकता होती है जो कि कक्षा का समर्थन नहीं करती है, तो उपर्युक्त दृष्टिकोण किसी भी वांछित अतिरिक्त अनुवाद को आसानी से समायोजित कर सकता है। – supercat