C # जेनरिक और टाइप चेकिंग


83

मेरे पास एक विधि है जो IList<T>एक पैरामीटर के रूप में उपयोग करती है । मुझे यह जांचने की आवश्यकता है कि उस Tवस्तु का प्रकार क्या है और इसके आधार पर कुछ करना है। मैं Tमूल्य का उपयोग करने की कोशिश कर रहा था , लेकिन संकलक इसे अनुमति नहीं देता है। मेरा समाधान निम्नलिखित है:

private static string BuildClause<T>(IList<T> clause)
{
    if (clause.Count > 0)
    {
        if (clause[0] is int || clause[0] is decimal)
        {
           //do something
        }
        else if (clause[0] is String)
        {
           //do something else
        }
        else if (...) //etc for all the types
        else
        {
           throw new ApplicationException("Invalid type");
        }
    } 
}

ऐसा करने के लिए एक बेहतर तरीका होना चाहिए। क्या कोई तरीका है जिससे मैं जांच कर सकता हूं Tकि किस प्रकार का मामला पारित हुआ है और फिर एक switchबयान का उपयोग करें ?


1
मैं व्यक्तिगत रूप से जानना चाहूंगा कि आप प्रत्येक डेटा प्रकार के लिए क्या विशेष कर रहे हैं। यदि आप प्रत्येक डेटा प्रकार के लिए लगभग एक ही परिवर्तन कर रहे हैं, तो विभिन्न प्रकारों को एक सामान्य इंटरफ़ेस में मैप करना और उस इंटरफ़ेस पर काम करना आसान हो सकता है।
जूलियट

जवाबों:


121

आप ओवरलोड का उपयोग कर सकते हैं:

public static string BuildClause(List<string> l){...}

public static string BuildClause(List<int> l){...}

public static string BuildClause<T>(List<T> l){...}

या आप सामान्य पैरामीटर के प्रकार का निरीक्षण कर सकते हैं:

Type listType = typeof(T);
if(listType == typeof(int)){...}

23
+1: ओवरलोड निश्चित रूप से डिजाइन और दीर्घकालिक स्थिरता के संदर्भ में यहां सबसे अच्छा समाधान है। एक सामान्य पैरामीटर का रनटाइम टाइप-चेक सीधे चेहरे के साथ कोड करने के लिए बहुत विडंबनापूर्ण लगता है।
जूलियट

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

क्षमा करें, क्या switch-caseइसके बजाए इसे प्राप्त करने का एक तरीका है if-else?
टीजीए

@HappyCoding दुर्भाग्य से नहीं = (आप C # के अगले संस्करण के साथ ऐसा करने में सक्षम हो सकते हैं।
jonnii

7
यदि आपके ओवरलोड कार्यात्मक रूप से भिन्न हैं (साइड-इफेक्ट पर भी विचार करें), तो आपको सामान्य अधिभार ( इस उत्तर को देखें ) पर भरोसा नहीं करना चाहिए , क्योंकि आप गारंटी नहीं दे सकते हैं कि एक अधिक विशिष्ट अधिभार कहा जाएगा। यहां नियम यह है: यदि आपको किसी विशिष्ट प्रकार के लिए विशेष तर्क करना है तो आपको उस प्रकार की जांच करनी होगी और ओवरलोडिंग का उपयोग नहीं करना चाहिए; हालाँकि, यदि आप केवल विशेष तर्क (यानी प्रदर्शन सुधार के लिए) करने के लिए PREFER करते हैं, लेकिन सामान्य मामले सहित सभी अधिभार एक ही परिणाम में होते हैं, तो आप टाइप चेकिंग के बजाय ओवरलोडिंग का उपयोग कर सकते हैं।
टोमोसियस

23

आप उपयोग कर सकते हैं typeof(T)

private static string BuildClause<T>(IList<T> clause)
{
     Type itemType = typeof(T);
     if(itemType == typeof(int) || itemType == typeof(decimal))
    ...
}

7

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

TypeSwitcher.Do(clause[0],
  TypeSwitch.Case<int>(x => ...),  // x is an int
  TypeSwitch.Case<decimal>(d => ...), // d is a decimal 
  TypeSwitch.Case<string>(s => ...)); // s is a string

पूर्ण ब्लॉग पोस्ट और कार्यान्वयन पर विवरण यहाँ उपलब्ध हैं


6

और, क्योंकि C # विकसित हो गया है, आप पैटर्न मिलान का उपयोग (अब) कर सकते हैं ।

private static string BuildClause<T>(IList<T> clause)
{
    if (clause.Count > 0)
    {
        switch (clause[0])
        {
            case int x: // do something with x, which is an int here...
            case decimal x: // do something with x, which is a decimal here...
            case string x: // do something with x, which is a string here...
            ...
            default: throw new ApplicationException("Invalid type");
        }
    }
}

और फिर से C # 8.0 में स्विच एक्सप्रेशंस के साथ, सिंटैक्स और भी अधिक रसीला हो जाता है।

private static string BuildClause<T>(IList<T> clause)
{
    if (clause.Count > 0)
    {
        return clause[0] switch
        {
            int x => "some string related to this int",
            decimal x => "some string related to this decimal",
            string x => x,
            ...,
            _ => throw new ApplicationException("Invalid type")
        }
    }
}

4

टाइपो ऑपरेटर ...

typeof(T)

... सी # स्विच स्टेटमेंट के साथ काम नहीं करेगा। लेकिन यह कैसे? निम्नलिखित पोस्ट में एक स्थिर वर्ग है ...

क्या इससे बेहतर विकल्प 'स्विच ऑन टाइप' है?

... जो आपको इस तरह कोड लिखने देगा:

TypeSwitch.Do(
    sender,
    TypeSwitch.Case<Button>(() => textBox1.Text = "Hit a Button"),
    TypeSwitch.Case<CheckBox>(x => textBox1.Text = "Checkbox is " + x.Checked),
    TypeSwitch.Default(() => textBox1.Text = "Not sure what is hovered over"));

यहां भी जारेडपर का जवाब देखें।
रॉबर्ट हार्वे

3

हर किसी के लिए जो कहता है कि प्रकारों की जाँच करना और प्रकार के आधार पर कुछ करना जेनेरिक के लिए एक महान विचार नहीं है मैं सहमत हूँ लेकिन मुझे लगता है कि कुछ परिस्थितियाँ हो सकती हैं जहाँ यह पूरी तरह से समझ में आता है।

उदाहरण के लिए यदि आपके पास एक ऐसा वर्ग है जो कहता है कि इसे लागू किया गया है (नोट: मुझे वह सब कुछ नहीं दिखाई दे रहा है जो यह कोड सरलता के लिए करता है और बस इसमें काट दिया जाता है और यहाँ चिपका दिया जाता है, इसलिए यह संपूर्ण कोड की तरह निर्माण या कार्य नहीं कर सकता है लेकिन यह बिंदु भर में हो जाता है। इसके अलावा, यूनिट एक एनम है):

public class FoodCount<TValue> : BaseFoodCount
{
    public TValue Value { get; set; }

    public override string ToString()
    {
        if (Value is decimal)
        {
            // Code not cleaned up yet
            // Some code and values defined in base class

            mstrValue = Value.ToString();
            decimal mdecValue;
            decimal.TryParse(mstrValue, out mdecValue);

            mstrValue = decimal.Round(mdecValue).ToString();

            mstrValue = mstrValue + mstrUnitOfMeasurement;
            return mstrValue;
        }
        else
        {
            // Simply return a string
            string str = Value.ToString() + mstrUnitOfMeasurement;
            return str;
        }
    }
}

...

public class SaturatedFat : FoodCountWithDailyValue<decimal>
{
    public SaturatedFat()
    {
        mUnit = Unit.g;
    }

}

public class Fiber : FoodCount<int>
{
    public Fiber()
    {
        mUnit = Unit.g;
    }
}

public void DoSomething()
{
       nutritionFields.SaturatedFat oSatFat = new nutritionFields.SaturatedFat();

       string mstrValueToDisplayPreFormatted= oSatFat.ToString();
}

इसलिए संक्षेप में, मुझे लगता है कि कुछ विशेष करने के लिए, यह देखने के लिए मान्य कारण हैं कि आप यह देखना चाहते हैं कि जेनेरिक किस प्रकार का है।


2

आप क्या करना चाहते हैं, इसके लिए स्विच स्टेटमेंट का उपयोग करने का कोई तरीका नहीं है। स्विच स्टेटमेंट को अभिन्न प्रकारों के साथ आपूर्ति की जानी चाहिए, जिसमें जटिल प्रकार जैसे "टाइप" ऑब्जेक्ट या उस मामले के लिए कोई अन्य ऑब्जेक्ट प्रकार शामिल नहीं है।


2

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


2

आप कर सकते हैं typeOf(T), लेकिन मैं आपकी विधि को दोगुना कर दूंगा और सुनिश्चित करूंगा कि आपकी एकल जिम्मेदारी का उल्लंघन न हो। यह एक कोड गंध होगा, और यह कहना नहीं है कि ऐसा नहीं किया जाना चाहिए लेकिन आपको सतर्क रहना चाहिए।

जेनरिक की बात यह है कि टाइप-अज्ञेयवादी अल्गोरिथम का निर्माण करने में सक्षम है क्या आप परवाह नहीं करते हैं कि प्रकार क्या है या जब तक यह मानदंड के एक निश्चित सेट के भीतर फिट बैठता है। आपका कार्यान्वयन बहुत सामान्य नहीं है।


2

मुझे उम्मीद है कि आप इस मददगार को खोज लेंगे:

  • typeof(IList<T>).IsGenericType == true
  • typeof(IList<T>).GetGenericTypeDefinition() == typeof(IList<>)
  • typeof(IList<int>).GetGenericArguments()[0] == typeof(int)

https://dotnetfiddle.net/5qUZnt


0

इस बारे में कैसा है :

            // Checks to see if the value passed is valid. 
            if (!TypeDescriptor.GetConverter(typeof(T)).IsValid(value))
            {
                throw new ArgumentException();
            }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.