सी # में जेनिक्स और टी


12

के स्थिर सदस्यों तक पहुंचने से मेरा प्रश्न सी # और स्टेटिक मेमर्स तक कैसे पहुंचेगा ... अच्छा मुझे नहीं पता कि इसे कैसे समझाया जाए (किस तरह के प्रश्न के लिए बुरा है यह नहीं है ?

Class test<T>{ 
    int method1(Obj Parameter1){ 
     //in here I want to do something which I would explain as 
     T.TryParse(Parameter1); 

     //my problem is that it does not work ... I get an error. 
     //just to explain: if I declare test<int> (with type Integer) 
     //I want my sample code to call int.TryParse(). If it were String 
     //it should have been String.TryParse() 
    } 
} 

तो अपने जवाब के लिए तुम लोगों को धन्यवाद (वैसे सवाल यह है कि द्वारा:: कैसे मैं एक त्रुटि हो रही बिना इस समस्या का समाधान होगा)) मैं तो बस आप कुछ नमूना कोड दे देंगे। यह शायद आपके लिए काफी आसान सवाल है!

धन्यवाद, निकलस


संपादित करें: अपने जवाब के लिए आप सभी धन्यवाद!

हालांकि मुझे लगता है कि कोशिश-पकड़ वाक्यांश सबसे सुरुचिपूर्ण है, मुझे अपने अनुभव से पता चलता है कि यह वास्तव में एक बमर हो सकता है। मैंने इसे एक बार इस्तेमाल किया और कार्यक्रम चलाने के लिए लगभग 30 मिनट लग गए, बाद में केवल गणना करने के लिए केवल 2 मिनट लग गए क्योंकि मैंने कोशिश-पकड़ से बचा था।

यही कारण है कि मैंने स्विच स्टेटमेंट को सबसे अच्छा जवाब चुना है। यह कोड को और अधिक जटिल बनाता है लेकिन दूसरी ओर मैं कल्पना करता हूं कि यह अपेक्षाकृत तेज़ और पढ़ने के लिए अपेक्षाकृत आसान हो। (हालांकि मैं अब भी लगता है कि वहाँ एक और अधिक सुरुचिपूर्ण तरीका होना चाहिए ... शायद अगले भाषा मैं जानने में: पी)


हालांकि आप कुछ अन्य सुझाव मैं अब भी इंतज़ार कर रहा हूँ है (और भाग लेने के इच्छुक)

2

मिश्रण में कुछ प्रतिबिंब यह करने के लिए एक और तरीका है, इस समय:

static class Parser 
{ 
    public static bool TryParse<TType>(string str, out TType x) 
    { 
     // Get the type on that TryParse shall be called 
     Type objType = typeof(TType); 

     // Enumerate the methods of TType 
     foreach(MethodInfo mi in objType.GetMethods()) 
     { 
      if(mi.Name == "TryParse") 
      { 
       // We found a TryParse method, check for the 2-parameter-signature 
       ParameterInfo[] pi = mi.GetParameters(); 
       if(pi.Length == 2) // Find TryParse(String, TType) 
       { 
        // Build a parameter list for the call 
        object[] paramList = new object[2] { str, default(TType) }; 

        // Invoke the static method 
        object ret = objType.InvokeMember("TryParse", BindingFlags.InvokeMethod, null, null, paramList); 

        // Get the output value from the parameter list 
        x = (TType)paramList[1]; 
        return (bool)ret; 
       } 
      } 
     } 

     // Maybe we should throw an exception here, because we were unable to find the TryParse 
     // method; this is not just a unable-to-parse error. 

     x = default(TType); 
     return false; 
    } 
} 

अगले कदम को लागू करने के

public static TRet CallStaticMethod<TRet>(object obj, string methodName, params object[] args); 

पूर्ण पैरामीटर प्रकार मिलान आदि के साथ की कोशिश कर रहा हो जाएगा

  0

यह अजीब धीमी है। आपको एक सामान्य प्रतिनिधि को कैश करना चाहिए। 03 jan. 112011-01-03 16:13:24


5

समस्या यह है कि TryParse को किसी इंटरफ़ेस या बेस क्लास पर कहीं भी परिभाषित नहीं किया गया है, इसलिए आप यह धारणा नहीं कर सकते कि आपके वर्ग में पारित प्रकार में यह कार्य होगा। जब तक कि आप किसी भी तरह से टी को बाधित नहीं कर सकते, आप इसमें बहुत भाग लेंगे।

Constraints on Type Parameters


0

आप शायद नहीं कर सकते करते हैं।

सबसे पहले यदि यह संभव हो तो आपको टी पर कड़े बाध्य होने की आवश्यकता होगी ताकि टाइपशेकर सुनिश्चित हो सके कि टी के लिए सभी संभावित प्रतिस्थापन वास्तव में TryParse नामक एक स्थिर विधि थी।


-1

यह नहीं है कि कैसे statics काम करते हैं। आपको वैश्विक वर्ग में तरह के रूप में सांख्यिकी के बारे में सोचना होगा, भले ही वे पूरे प्रकार के समूह में फैले हुए हों। मेरी सिफारिश यह है कि इसे टी इंस्टेंस के अंदर एक संपत्ति बनाना है जो आवश्यक स्थिर विधि तक पहुंच सकता है।

इसके अलावा टी कुछ का वास्तविक उदाहरण है, और किसी भी अन्य उदाहरण की तरह आप तत्काल मूल्य के माध्यम से उस प्रकार के लिए statics तक पहुंचने में सक्षम नहीं हैं। यहाँ क्या करना है का एक उदाहरण है:

class a { 
    static StaticMethod1() 
    virtual Method1() 
} 

class b : a { 
    override Method1() return StaticMethod1() 
} 

class c : a { 
    override Method1() return "XYZ" 
} 

class generic<T> 
    where T : a { 
    void DoSomething() T.Method1() 
} 

3

एक विशिष्ट वर्ग या अंतरफलक के एक सदस्य आप कहाँ कीवर्ड का उपयोग करें और इंटरफ़ेस या आधार वर्ग विधि है कि निर्दिष्ट करने की आवश्यकता का उपयोग करने के।

उपर्युक्त उदाहरण में TryParse इंटरफ़ेस या बेस क्लास से नहीं आता है, तो आप ऊपर क्या करने का प्रयास कर रहे हैं, यह संभव नहीं है। सबसे अच्छा बस कनवर्ट करें। चेंज टाइप और एक कोशिश/पकड़ विवरण।

class test<T> 
{ 
    T Method(object P) 
    { 
     try { 
      return (T)Convert.ChangeType(P, typeof(T)); 
     } catch(Exception e) { 
      return null; 
     } 
    } 
} 

0

आप मेरी पिछली पोस्ट limiting generic types to primitives पर पढ़ना चाह सकते हैं।यह आपको जेनेरिक को पारित करने के लिए कुछ पॉइंटर्स दे सकता है (टाइपपर्स स्पष्ट रूप से केवल प्राइमेटिव्स (स्ट्रिंग) की एक निश्चित संख्या के लिए उपलब्ध है। ट्रीपार्स स्पष्ट रूप से अपवाद है, जो समझ में नहीं आता है) ।

एक बार जब आप प्रकार पर एक संभाल के और अधिक है, तो आप तो यह पार्स करने का प्रयास करने पर काम कर सकते हैं। आप (सही TryParse कॉल करने के लिए) वहाँ में एक बदसूरत स्विच का एक सा आवश्यकता हो सकती है, लेकिन मैं आपको लगता है वांछित कार्यक्षमता प्राप्त कर सकते हैं।

यदि आपको ऊपर दिए गए किसी भी को समझाने की आवश्यकता है, तो कृपया पूछें :)


1

आप इस तरह कुछ करने के लिए इसका मतलब है:

Class test<T> 
{ 
    T method1(object Parameter1){ 

     if(Parameter1 is T) 
     { 
       T value = (T) Parameter1; 
      //do something with value 
      return value; 
     } 
     else 
     { 
      //Parameter1 is not a T 
      return default(T); //or throw exception 
     } 
    } 
} 

दुर्भाग्य से आप TryParse पैटर्न के लिए जांच नहीं कर सकता के रूप में यह स्थिर है - जो दुर्भाग्य से मतलब है कि यह विशेष रूप से अच्छी तरह से जेनरिक के लिए अनुकूल नहीं है।


1

आप वास्तव में क्या देख रहे हैं करने के लिए एक ही रास्ता है, तो विधि टी

एक अन्य विकल्प यह सुनिश्चित करें कि वस्तु आप में भेजने के लिए मौजूद है की जाँच करने के प्रतिबिंब का उपयोग करने के लिए किया जाएगा द्वारा एक परिवर्तनीय वस्तु है टाइप को आईकोनवर्टेबल (सभी आदिम प्रकारों को आईकोनवर्टिबल लागू) में रोकना। यह आपको अपने पैरामीटर को दिए गए प्रकार में बहुत लचीला रूप से परिवर्तित करने की अनुमति देगा।

Class test<T> 
{ 
    int method1(IConvertible Parameter1){ 

     IFormatProvider provider = System.Globalization.CultureInfo.CurrentCulture.GetFormat(typeof(T)); 

     T temp = Parameter1.ToType(typeof(T), provider); 
    } 
} 

आप मूल रूप से 'ऑब्जेक्ट' प्रकार का उपयोग करके इस पर एक भिन्नता भी कर सकते हैं।

Class test<T> 
{ 
    int method1(object Parameter1){ 

     if(Parameter1 is IConvertible) { 

      IFormatProvider provider = System.Globalization.CultureInfo.CurrentCulture.GetFormat(typeof(T)); 

      T temp = Parameter1.ToType(typeof(T), provider); 

     } else { 
      // Do something else 
     } 
    } 
} 

3

संक्षिप्त उत्तर, आप नहीं कर सकते।

लांग जवाब है, आप को धोखा देने के कर सकते हैं:

public class Example 
{ 
    internal static class Support 
    { 
     private delegate bool GenericParser<T>(string s, out T o); 
     private static Dictionary<Type, object> parsers = 
      MakeStandardParsers(); 
     private static Dictionary<Type, object> MakeStandardParsers() 
     { 
      Dictionary<Type, object> d = new Dictionary<Type, object>(); 
      // You need to add an entry for every type you want to cope with. 
      d[typeof(int)] = new GenericParser<int>(int.TryParse); 
      d[typeof(long)] = new GenericParser<long>(long.TryParse); 
      d[typeof(float)] = new GenericParser<float>(float.TryParse); 
      return d; 
     } 
     public static bool TryParse<T>(string s, out T result) 
     { 
      return ((GenericParser<T>)parsers[typeof(T)])(s, out result); 
     } 
    } 
    public class Test<T> 
    { 
     public static T method1(string s) 
     { 
      T value; 
      bool success = Support.TryParse(s, out value); 
      return value; 
     } 
    } 
    public static void Main() 
    { 
     Console.WriteLine(Test<int>.method1("23")); 
     Console.WriteLine(Test<float>.method1("23.4")); 
     Console.WriteLine(Test<long>.method1("99999999999999")); 
     Console.ReadLine(); 
    } 
} 

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


2

ठीक है दोस्तों: सभी मछलियों के लिए धन्यवाद। अब आपके उत्तरों और मेरे शोध के साथ (विशेष रूप से limiting generic types to primitives पर लेख) मैं आपको अपना समाधान पेश करूंगा।

Class a<T>{ 
    private void checkWetherTypeIsOK() 
    { 
     if (T is int || T is float //|| ... any other types you want to be allowed){ 
      return true; 
     } 
     else { 
      throw new exception(); 
     } 
    } 
    public static a(){ 
     ccheckWetherTypeIsOK(); 
    } 
} 

0

सर्वश्रेष्ठ कोड: टी को वैल्यू टाइप करें इस प्रकार टाइप करें:

class test1<T> where T: struct 

यहां "संरचना" का अर्थ एक मान प्रकार है। स्ट्रिंग एक वर्ग है, मूल्य प्रकार नहीं। int, float, Enums सभी मान प्रकार हैं।

btw संकलक स्थिर तरीकों या 'प्रकार पैरामीटर' पर स्थिर पहुँच सदस्यों कॉल करने के लिए स्वीकार नहीं करता है कि निम्न उदाहरण में की तरह जो संकलन नहीं होगा :(

class MyStatic { public static int MyValue=0; } 
class Test<T> where T: MyStatic 
{ 
    public void TheTest() { T.MyValue++; } 
} 

=> त्रुटि 1 'टी' एक है 'प्रकार पैरामीटर' है, जो दिए गए संदर्भ में मान्य नहीं है

SL


1

यह वास्तव में एक समाधान नहीं है, लेकिन कुछ निश्चित परिस्थितियों में यह एक अच्छा विकल्प हो सकता है:। हम के लिए एक अतिरिक्त प्रतिनिधि पारित कर सकते हैं सामान्य विधि

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

public class Example 
{ 
    // ... 

    public static void PostInitCallback(Example example) 
    { 
     // Do something with the object... 
    } 
} 

और निम्न स्थिर विधि:

निम्नलिखित सरल वर्ग पर विचार करें

public static T CreateAndInit<T>() where T : new() 
{ 
    var t = new T(); 
    // Some initialization code... 
    return t; 
} 

तो अभी हम क्या करना होगा:

var example = CreateAndInit<Example>(); 
Example.PostInitCallback(example); 

हालांकि, हम कर सकते थे एक अतिरिक्त प्रतिनिधि लेने के लिए हमारी विधि बदलें:

public delegate void PostInitCallback<T>(T t); 
public static T CreateAndInit<T>(PostInitCallback<T> callback) where T : new() 
{ 
    var t = new T(); 
    // Some initialization code... 
    callback(t); 
    return t; 
} 

और अब हम करने के लिए कॉल बदल सकते हैं:

var example = CreateAndInit<Example>(Example.PostInitCallback); 

जाहिर है यह बहुत ही विशिष्ट स्थितियों में ही उपयोगी है। लेकिन यह इस अर्थ में सबसे साफ समाधान है कि हम संकलन समय सुरक्षा प्राप्त करते हैं, इसमें कोई "हैकिंग" शामिल नहीं है, और कोड मृत सरल है।