2010-03-04 8 views
39

मेरे पास एक प्रकार, एक स्ट्रिंग और ऑब्जेक्ट है।सी #: सिस्टम से गतिशील पार्स। टाइप

क्या कोई तरीका है कि मैं पार्स विधि को कॉल कर सकता हूं या स्ट्रिंग पर गतिशील रूप से उस प्रकार के रूप में परिवर्तित कर सकता हूं?

मूल रूप से

कैसे मैं इस तर्क

object value = new object();  
String myString = "something"; 
Type propType = p.PropertyType; 

if(propType == Type.GetType("DateTime")) 
{ 
    value = DateTime.Parse(myString); 
} 

if (propType == Type.GetType("int")) 
{ 
    value = int.Parse(myString); 
} 

में अगर बयान को हटाने और अधिक इस तरह someting करते हो।

object value = new object(); 
String myString = "something"; 
Type propType = p.PropertyType; 


//this doesn't actually work 
value = propType .Parse(myString); 
+1

आप यह नहीं दिखाते कि कैसे 'पी' परिभाषित किया गया है, टाइपो? – Hogan

+1

कम से कम, आपको 'is' ऑपरेटर का उपयोग करना चाहिए। मैंने प्रतिबिंब का उपयोग किये बिना सही तरीके से जांचने के लिए अपना प्रश्न अपडेट कर दिया है। –

+3

@ डेविड पेफर, 'is' ऑपरेटर गलत तरीके से लागू किया गया है। इस संदर्भ में 'is' सच कभी वापस नहीं आएगा [' propType' हमेशा टाइप प्रकार 'प्रकार' होगा। आप 'propType == टाइपऑफ (डेटटाइम)' –

उत्तर

65

बचाव के लिए TypeDescriptor:

var converter = TypeDescriptor.GetConverter(propType); 
var result = converter.ConvertFrom(myString); 

TypeConverter बुनियादी ढांचे में एकीकृत, अपने खुद के TypeConverter को लागू करने और वर्ग को सजाने के लिए, TypeConverterAttribute

+2

आप डेटटाइम को कैसे सजाएंगे यह विशेषता या डिफ़ॉल्ट रूप से सजाया गया है? – Lazarus

+0

चूंकि आपको कक्षा को रूपांतरित करने के लिए सजाने की आवश्यकता है, क्या यह उनके उदाहरण के लिए काम नहीं करेगा जहां वह अंतर्निहित प्रकारों के लिए ऐसा करने की कोशिश कर रहा है? – Davy8

+0

वाह, ठंडा। 'TypeDescriptor' के बारे में नहीं पता था। क्या आप उपयोग पर विस्तार कर सकते हैं? शायद हमें एक फ्रेमवर्क मूल्य प्रकार, और एक जटिल कस्टम प्रकार के साथ एक ठोस उपयोग के माध्यम से ले? मुझे यह देखना बहुत अच्छा लगेगा कि –

13

इस के साथ साथ परिवर्तित किया सभी आदिम प्रकार के लिए काम करना चाहिए और IConvertible

लागू करने वाले प्रकारों के लिए 210

संपादित करें: वास्तव में आपके मामले में, आप जेनेरिक (कम से कम नहीं) का उपयोग नहीं कर सकते हैं। इसके बजाय तुम कर सकते हो कि:

object value = Convert.ChangeType(myString, propType); 
+0

आप टाइप प्रोपे टाइप से propType.Parse (myString) से कैसे प्राप्त करेंगे? मैं यह देखने के लिए उत्सुक हूं कि यह कैसे काम करता है। – Lazarus

+0

मैंने अभी अपना जवाब –

0

यह एक स्ट्रिंग को देखने के लिए तकनीकी रूप से असंभव है, और कुछ जो टाइप यह प्रतिनिधित्व के लिए पता है।

तो, किसी भी सामान्य दृष्टिकोण के लिए आपको कम से कम की आवश्यकता होगी:

  1. स्ट्रिंग
  2. प्रकार पार्स के लिए इस्तेमाल किया पार्स किया जा सकता।

स्थिर Convert.ChangeType() विधि पर एक नज़र डालें।

+3

तकनीकी रूप से बोलते हुए अद्यतन किया है, वह * स्ट्रिंग से * ऑब्जेक्ट प्रकार को * infer करने का प्रयास नहीं कर रहा है। उसे ऑब्जेक्ट प्रकार दिया जाता है, और एक प्रतिनिधित्व [स्ट्रिंग] से दूसरे [अच्छी तरह से परिभाषित ऑब्जेक्ट इंस्टेंस] में जाना चाहता है –

-1

यह क्या आप जो सी #

में नहीं है ऐसा करने के लिए (कम से कम अगर प्रकार शामिल आप स्रोत बदल नहीं सकते हैं करने के लिए प्रकार के होते हैं) duck typing की आवश्यकता होगी चाहते हैं की तरह लगता है कि आप यह करना की जरूरत है बहुत कुछ, मैं तर्क को उस वर्ग या विधि में लपेटूंगा जिसे आप "myString" और "propType" पास कर सकते हैं और यह मूल्य वापस कर देगा। उस विधि में आप केवल उस श्रृंखला को करेंगे जो आपके ऊपर है और जब वह मेल खाता है तो वह मान वापस कर देगा। आपको अभी भी सभी संभावित प्रकारों को मैन्युअल रूप से सूचीबद्ध करना होगा, लेकिन आपको केवल एक बार ऐसा करना होगा।

2

आप जो हासिल करना चाहते हैं उस पर निर्भर करता है।

1) यदि आप बस अपने कोड को साफ, और दोहराव प्रकार की जाँच के दूर करने के लिए कोशिश कर रहे हैं, तो क्या आप क्या करना चाहते एक विधि में अपने चेक को केंद्रीकृत, comme है

public static T To<T> (this string stringValue) 
{ 
    T value = default (T); 

    if (typeof (T) == typeof (DateTime)) 
    { 
     // insert custom or convention System.DateTime 
     // deserialization here ... 
    } 
    // ... add other explicit support here 
    else 
    { 
     throw new NotSupportedException (
      string.Format (
      "Cannot convert type [{0}] with value [{1}] to type [{2}]." + 
      " [{2}] is not supported.", 
      stringValue.GetType(), 
      stringValue, 
      typeof (T))); 
    } 

    return value; 
} 

2) यदि आप होगा मूल प्रकारों के लिए कुछ अधिक सामान्यीकृत की तरह, आप Thomas Levesquesuggests जैसे कुछ कोशिश कर सकते हैं - हालांकि सच में, मैंने इसे स्वयं नहीं किया है, मैं हाल ही में [हालिया?] एक्सटेंशन से Convert पर अपरिचित हूं। यह भी एक बहुत अच्छा सुझाव है।

3) असल में, आप शायद 1) और 2 दोनों को एक ही एक्सटेंशन में विलय करना चाहते हैं जो आपको मूल मूल्य रूपांतरण और स्पष्ट जटिल प्रकार के समर्थन का समर्थन करने में सक्षम बनाता है।

4) यदि आप पूरी तरह से "हाथ मुक्त" होना चाहते हैं, तो आप सादे पुराने deserialization [एक्सएमएल या बाइनरी, या तो/या] के लिए भी डिफ़ॉल्ट हो सकता है। बेशक, यह आपके इनपुट को बाधित करता है - यानी सभी इनपुट एक्सएमएल या बाइनरी प्रारूप को अनुमोदित होना चाहिए। ईमानदारी से, यह शायद अधिक है, लेकिन उल्लेख करने लायक है।

बेशक, ये सभी विधियां अनिवार्य रूप से वही काम करती हैं। उनमें से किसी भी में कोई जादू नहीं है, किसी बिंदु पर कोई एक रैखिक लुकअप कर रहा है [चाहे यह अनुक्रमिक if-clauses या .Net रूपांतरण और क्रमबद्धता सुविधाओं के माध्यम से हुड के नीचे एक अंतर्निहित रूप है।

5) यदि आप प्रदर्शन में सुधार करना चाहते हैं, तो आप अपनी रूपांतरण प्रक्रिया के "लुकअप" भाग को बेहतर बनाना चाहते हैं। एक स्पष्ट "समर्थित प्रकार" सूची बनाएं, प्रत्येक प्रकार किसी सरणी में किसी अनुक्रमणिका से संबंधित है। कॉल पर टाइप निर्दिष्ट करने के बजाय, आप इंडेक्स निर्दिष्ट करते हैं।

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

// S == source type 
// T == target type 
public interface IConvert<S> 
{ 
    // consumers\infrastructure may now add support 
    int AddConversion<T> (Func<S, T> conversion); 

    // gets conversion method for local consumption 
    Func<S, T> GetConversion<T>(); 

    // easy to use, linear look up for one-off conversions 
    T To<T> (S value); 
} 

public class Convert<S> : IConvert<S> 
{ 

    private class ConversionRule 
    { 
     public Type SupportedType { get; set; } 
     public Func<S, object> Conversion { get; set; } 
    } 

    private readonly List<ConversionRule> _map = new List<ConversionRule>(); 
    private readonly object _syncRoot = new object(); 

    public void AddConversion<T> (Func<S, T> conversion) 
    { 
     lock (_syncRoot) 
     { 
      if (_map.Any (c => c.SupportedType.Equals (typeof (T)))) 
      { 
       throw new ArgumentException (
        string.Format (
        "Conversion from [{0}] to [{1}] already exists. " + 
        "Cannot add new conversion.", 
        typeof (S), 
        typeof (T))); 
      } 

      ConversionRule conversionRule = new ConversionRule 
      { 
       SupportedType = typeof(T), 
       Conversion = (s) => conversion (s), 
      }; 
      _map.Add (conversionRule); 
     } 
    } 

    public Func<S, T> GetConversion<T>() 
    { 
     Func<S, T> conversionMethod = null; 

     lock (_syncRoot) 
     { 
      ConversionRule conversion = _map. 
       SingleOrDefault (c => c.SupportedType.Equals (typeof (T))); 

      if (conversion == null) 
      { 
       throw new NotSupportedException (
        string.Format (
        "Conversion from [{0}] to [{1}] is not supported. " + 
        "Cannot get conversion.", 
        typeof (S), 
        typeof (T))); 
      } 

      conversionMethod = 
       (value) => ConvertWrap<T> (conversion.Conversion, value); 
     } 

     return conversionMethod; 
    } 

    public T To<T> (S value) 
    { 
     Func<S, T> conversion = GetConversion<T>(); 
     T typedValue = conversion (value); 
     return typedValue; 
    } 

    // private methods 

    private T ConvertWrap<T> (Func<S, object> conversion, S value) 
    { 
     object untypedValue = null; 
     try 
     { 
      untypedValue = conversion (value); 
     } 
     catch (Exception exception) 
     { 
      throw new ArgumentException (
       string.Format (
       "Unexpected exception encountered during conversion. " + 
       "Cannot convert [{0}] [{1}] to [{2}].", 
       typeof (S), 
       value, 
       typeof (T)), 
       exception); 
     } 

     if (!(untypedValue is T)) 
     { 
      throw new InvalidCastException (
       string.Format (
       "Converted [{0}] [{1}] to [{2}] [{3}], " + 
       "not of expected type [{4}]. Conversion failed.", 
       typeof (S), 
       value, 
       untypedValue.GetType(), 
       untypedValue, 
       typeof (T))); 
     } 

     T typedValue = (T)(untypedValue); 

     return typedValue; 
    } 

} 

और यह

// as part of application innitialization 
IConvert<string> stringConverter = container.Resolve<IConvert<string>>(); 
stringConverter.AddConversion<int> (s => Convert.ToInt32 (s)); 
stringConverter.AddConversion<Color> (s => CustomColorParser (s)); 

... 

// a consumer elsewhere in code, say a Command acting on 
// string input fields of a form 
// 
// NOTE: stringConverter could be injected as part of DI 
// framework, or obtained directly from IoC container as above 
int someCount = stringConverter.To<int> (someCountString); 

Func<string, Color> ToColor = stringConverter.GetConversion <Color>(); 
IEnumerable<Color> colors = colorStrings.Select (s => ToColor (s)); 
के रूप में इस्तेमाल किया जाएगा

मैं इस बाद के दृष्टिकोण को बहुत पसंद करता हूं, क्योंकि यह आपको पूर्ण रूपांतरण पर नियंत्रण देता है। यदि आप कैसल विंडसर या यूनिटी जैसे नियंत्रण का एक उलटा [आईओसी] कंटेनर का उपयोग करते हैं, तो इस सेवा का इंजेक्शन आपके लिए किया जाता है। इसके अलावा, क्योंकि यह उदाहरण आधारित है, तो आपके पास कई उदाहरण हो सकते हैं, प्रत्येक के अपने रूपांतरण नियमों के सेट के साथ - यदि उदाहरण के लिए, आपके पास एकाधिक उपयोगकर्ता नियंत्रण हैं, प्रत्येक का अपना DateTime या अन्य जटिल स्ट्रिंग प्रारूप उत्पन्न होता है।

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

6

मैं इस समस्या का सामना किया है और इस मैं इसे कैसे हल किया है:

value = myString; 
var parse = propType.GetMethod("Parse", new[] { typeof(string) }); 
if (parse != null) { 
    value = parse.Invoke(null, new object[] { value }); 
} 

... और यह मेरे लिए काम किया।

इसे समेटने के लिए, आप ऑब्जेक्ट प्रकार पर एक स्थिर "पार्स" विधि ढूंढने का प्रयास कर रहे हैं जो तर्क के रूप में केवल एक स्ट्रिंग लेता है। यदि आपको ऐसी विधि मिलती है, तो उसे उस स्ट्रिंग पैरामीटर के साथ आज़माएं जिसे आप कनवर्ट करने का प्रयास कर रहे हैं। चूंकि पी मेरे प्रकार के लिए प्रॉपर्टीइन्फो है, इसलिए मैंने अपना उदाहरण इस प्रकार मान के साथ सेट किया है:

p.SetValue(instance, value, null); 
संबंधित मुद्दे