2008-09-16 5 views
22

उदाहरण है:कैसे पता लगाने के लिए अगर प्रकार एक और जेनेरिक प्रकार

public static void DoSomething<K,V>(IDictionary<K,V> items) { 
    items.Keys.Each(key => { 
     if (items[key] **is IEnumerable<?>**) { /* do something */ } 
     else { /* do something else */ } 
} 

इस प्रतिबिंब का उपयोग कर के बिना किया जा सकता है? मैं सी # में IENumerable कैसे कहूँ? क्या मुझे IENumerable < के बाद से IENumerable का उपयोग करना चाहिए> IENumerable लागू करता है?

उत्तर

35

The previously accepted answer अच्छा है, लेकिन यह गलत है। शुक्र है, त्रुटि एक छोटा सा है। IEnumerable के लिए जांच करना पर्याप्त नहीं है यदि आप वास्तव में इंटरफ़ेस के सामान्य संस्करण के बारे में जानना चाहते हैं; वहां बहुत सारी कक्षाएं हैं जो केवल नोंगनेरिक इंटरफ़ेस को लागू करती हैं। मैं एक मिनट में जवाब दूंगा। हालांकि, सबसे पहले मैं कहना है कि स्वीकार किए जाते हैं जवाब बहुत ज्यादा जटिल है, चाहते हैं के बाद से निम्नलिखित कोड दिया परिस्थितियों में एक ही प्राप्त होगा:

if (items[key] is IEnumerable) 

यह करता है और भी अधिक है, क्योंकि यह अलग से प्रत्येक आइटम के लिए काम करता है (और उनके सामान्य उप-वर्ग पर नहीं, V)।

अब, सही समाधान के लिए।

static bool IsGenericEnumerable(Type t) { 
    var genArgs = t.GetGenericArguments(); 
    if (genArgs.Length == 1 && 
      typeof(IEnumerable<>).MakeGenericType(genArgs).IsAssignableFrom(t)) 
     return true; 
    else 
     return t.BaseType != null && IsGenericEnumerable(t.BaseType); 
} 

आप आसानी से इस कोड की सत्यता की जांच कर सकते हैं: यह थोड़ा और अधिक जटिल है क्योंकि हम सामान्य प्रकार IEnumerable`1 (अर्थात, एक प्रकार पैरामीटर के साथ प्रकार IEnumerable<> है) लेने के लिए और सही सामान्य तर्क इंजेक्षन करने के लिए है है :

var xs = new List<string>(); 
var ys = new System.Collections.ArrayList(); 
Console.WriteLine(IsGenericEnumerable(xs.GetType())); 
Console.WriteLine(IsGenericEnumerable(ys.GetType())); 

पैदावार:

True 
False 

ज्यादा तथ्य यह है कि इस प्रतिबिंब का उपयोग करता है से चिंतित न हो। हालांकि यह सच है कि यह रनटाइम ओवरहेड जोड़ता है, इसलिए is ऑपरेटर का उपयोग करता है।

बेशक उपर्युक्त कोड बहुत ही सीमित है और इसे अधिक सामान्य रूप से लागू विधि, IsAssignableToGenericType में विस्तारित किया जा सकता है। निम्नलिखित कार्यान्वयन थोड़ा गलत है और मैं इसे यहाँ ऐतिहासिक प्रयोजनों के लिए ही के लिए छोड़ देंगे। इसका उपयोग न करें।इसके बजाय, James has provided an excellent, correct implementation in his answer.

public static bool IsAssignableToGenericType(Type givenType, Type genericType) { 
    var interfaceTypes = givenType.GetInterfaces(); 

    foreach (var it in interfaceTypes) 
     if (it.IsGenericType) 
      if (it.GetGenericTypeDefinition() == genericType) return true; 

    Type baseType = givenType.BaseType; 
    if (baseType == null) return false; 

    return baseType.IsGenericType && 
     baseType.GetGenericTypeDefinition() == genericType || 
     IsAssignableToGenericType(baseType, genericType); 
} 

यह विफल रहता है जब genericTypegivenType रूप में ही है; एक ही कारण के लिए, यह अर्थात

IsAssignableToGenericType(typeof(List<int>), typeof(List<>)) == false 
IsAssignableToGenericType(typeof(int?), typeof(Nullable<>)) == false 

नल प्रकार के लिए विफल रहता है, मैं एक gist with a comprehensive suite of test cases बना लिया है।

+0

यह वही है जो मैं ढूंढ रहा था। धन्यवाद! यह अभी भी प्रतिबिंब का उपयोग करता है, लेकिन मैं "टाइपऑफ (आईनेमरेबल <>)" वाक्यविन्यास की तलाश में था। धन्यवाद! –

+0

क्या "है" के अलावा अन्य प्रकार के चेक करने का कोई और तरीका नहीं है? –

+0

अंतिम पंक्ति को 'रिटर्न (बेसटाइप.इसेजनेरिक टाइप और बेसटाइप.गेटजेनरिक टाइप टाइपिनिशन() == जेनेरिक टाइप) पढ़ना चाहिए || IsAssignableToGenericType (बेस टाइप, जेनेरिक टाइप); 'आप रिकर्सन से बच नहीं सकते क्योंकि बेस क्लास सामान्य है। –

4
if (typeof(IEnumerable).IsAssignableFrom(typeof(V))) { 
+0

प्रतिबिंब का उपयोग करता है। यह काम करता है ... लेकिन मैं सोच रहा था कि क्या आप इसे प्रतिबिंब के बिना कर सकते हैं, विशेष रूप से आप कैसे कहते हैं कि C# में एक विशिष्टीकृत सामान्य प्रकार निर्दिष्ट करें। धन्यवाद! –

+0

मुझे इतना यकीन नहीं है। मुझे लगता है कि रनटाइम में टाइप सिस्टम में पर्याप्त जानकारी है जो असेंबली को प्रतिबिंबित किए बिना IsAssignable से सवाल पूछने में सक्षम है। –

0

मुझे यकीन नहीं है कि मैं समझता हूं कि आप यहां क्या मतलब है। आप अगर वस्तु किसी भी सामान्य प्रकार की है पता करने के लिए चाहते हैं या आप अगर यह एक विशिष्ट सामान्य प्रकार है परीक्षण करना चाहते हैं करते हैं? या आप सिर्फ जानना चाहते हैं कि संख्यात्मक है या नहीं?

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

इसके अलावा, आप प्रकार पर 'है' ऑपरेटर का उपयोग नहीं कर सकते।

// Not allowed 
if (string is Object) 
    Foo(); 
// You have to use 
if (typeof(object).IsAssignableFrom(typeof(string)) 
    Foo(); 

अधिक जानकारी के लिए this question about types देखें। शायद यह आपकी मदद करेगा।

+0

मैं जानना चाहता हूं कि मेरे उदाहरण में वी कुछ भी IENumerable लागू करता है, और मुझे परवाह नहीं है। मुझे यह कहना चाहिए था कि आइटम [कुंजी] मेरे उदाहरण में IENumerable है। इसे पकड़ने के लिए धन्यवाद। –

1

मैं अधिक भार का उपयोग करेंगे की जांच करना चाह:

public static void DoSomething<K,V>(IDictionary<K,V> items) 
    where V : IEnumerable 
{ 
    items.Keys.Each(key => { /* do something */ }); 
} 

public static void DoSomething<K,V>(IDictionary<K,V> items) 
{ 
    items.Keys.Each(key => { /* do something else */ }); 
} 
5

सामान्य प्रकार के बारे में चेतावनी और IsAssignableFrom() का उपयोग कर का एक शब्द है ...

आप निम्नलिखित है कहते हैं:

public class MyListBase<T> : IEnumerable<T> where T : ItemBase 
{ 
} 

public class MyItem : ItemBase 
{ 
} 

public class MyDerivedList : MyListBase<MyItem> 
{ 
} 

आधार सूची प्रकार पर या व्युत्पन्न सूची प्रकार पर IsAssignableFrom कॉलिंग होगा झूठी वापसी, फिर भी स्पष्ट रूप से MyDerivedList विरासत MyListBase<T>। (जेफ के लिए एक त्वरित नोट, जेनेरिक बिल्कुल को <T> प्राप्त करने के लिए कोड ब्लॉक या टिल्ड्स में लपेटा जाना चाहिए, अन्यथा यह छोड़ा गया है। क्या यह इरादा है?) समस्या इस तथ्य से उत्पन्न होती है कि MyListBase<MyItem> को पूरी तरह से अलग प्रकार के रूप में माना जाता है MyListBase<T>। निम्नलिखित लेख इसे थोड़ा बेहतर समझा सकता है।

public static bool IsDerivedFromGenericType(Type givenType, Type genericType) 
    { 
     Type baseType = givenType.BaseType; 
     if (baseType == null) return false; 
     if (baseType.IsGenericType) 
     { 
      if (baseType.GetGenericTypeDefinition() == genericType) return true; 
     } 
     return IsDerivedFromGenericType(baseType, genericType); 
    } 

/संपादित करें: कोनराड के नए पद जो के साथ-साथ इंटरफेस स्थान पर है खाते में सामान्य प्रत्यावर्तन लेता http://mikehadlow.blogspot.com/2006/08/reflecting-generics.html

इसके बजाय, निम्न पुनरावर्ती क्रिया का प्रयास करें। बहुत अच्छा काम। :)

/EDIT2: यदि जेनेरिक टाइप एक इंटरफ़ेस है या नहीं, तो प्रदर्शन लाभों को महसूस किया जा सकता है या नहीं। जांच किया जा सकता है एक अगर वर्तमान इंटरफ़ेस कोड चारों ओर ब्लॉक, लेकिन अगर आप .NET 3.5 का उपयोग करने में रुचि रखते हैं, मेरा एक दोस्त प्रदान करता है निम्नलिखित:

public static bool IsAssignableToGenericType(Type givenType, Type genericType) 
    { 
     var interfaces = givenType.GetInterfaces().Where(it => it.IsGenericType).Select(it => it.GetGenericTypeDefinition()); 
     var foundInterface = interfaces.FirstOrDefault(it => it == genericType); 
     if (foundInterface != null) return true; 

     Type baseType = givenType.BaseType; 
     if (baseType == null) return false; 

     return baseType.IsGenericType ? 
      baseType.GetGenericTypeDefinition() == genericType : 
      IsAssignableToGenericType(baseType, genericType); 
    } 
+0

जेफ को नोट करें: यह डिज़ाइन द्वारा है क्योंकि मार्कडाउन इनलाइन HTML की अनुमति देता है। आपकी सूचना के लिए धन्यवाद, मैं इसे अपने कोड में एकीकृत कर दूंगा। –

+0

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

+0

जेम्स के उत्तर को बेहतर उत्तर के लिए देखें: http://stackoverflow.com/a/1075059/320623 – Joel

0

मैं ऐसी स्थिति व्याख्या करने योग्य हो सकता है सोचता था एक तरह से @Thomas Danecker के solution के समान है, लेकिन एक और टेम्पलेट तर्क जोड़ने में:

public static void DoSomething<K, V, U>(IDictionary<K,V> items) 
    where V : IEnumerable<U> { /* do something */ } 
public static void DoSomething<K, V>(IDictionary<K,V> items) 
          { /* do something else */ } 

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

अगर कोई भी गलत कुछ बता सकता है तो मैं बहुत आभारी हूं, मैंने यहां किया होगा।

82

इस पोस्ट के लिए बहुत बहुत धन्यवाद। मैं कोनराड रुडॉल्फ के समाधान का एक संस्करण प्रदान करना चाहता था जिसने मेरे लिए बेहतर काम किया है। मेरे पास उस संस्करण के साथ मामूली समस्याएं थीं, विशेष रूप से जब कोई टाइप एक शून्य मूल्य प्रकार का परीक्षण करता है:

public static bool IsAssignableToGenericType(Type givenType, Type genericType) 
{ 
    var interfaceTypes = givenType.GetInterfaces(); 

    foreach (var it in interfaceTypes) 
    { 
     if (it.IsGenericType && it.GetGenericTypeDefinition() == genericType) 
      return true; 
    } 

    if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType) 
     return true; 

    Type baseType = givenType.BaseType; 
    if (baseType == null) return false; 

    return IsAssignableToGenericType(baseType, genericType); 
} 
+1

शानदार काम! । 'सार्वजनिक स्थैतिक bool IsAssignableToGenericType (इस प्रकार प्रकार, प्रकार genericType) { वापसी type.GetInterfaces() किसी भी (यह => it.IsGenericType && it.GetGenericTypeDefinition (: यहाँ जो संक्षिप्तता पसंद करते हैं लोगों के लिए एक छोटी (ईआर) संस्करण है) == जेनेरिक टाइप) || (type.IsGenericType && type.GetGenericTypeDefinition() == जेनेरिक टाइप) || (type.BaseType! = null && isAssignableToGenericType (type.BaseType, जेनेरिक टाइप)); } ' – Moeri

+3

यह बहुत अच्छा है, लेकिन ऐसा लगता है कि अगर आप प्रत्यक्ष तुलना मिलान करते हैं तो इंटरफ़ेस तुलना से सीधे तुलना करने के लिए प्रत्यक्ष तुलना में यह बहुत तेज होगा? – Shazwazza

+1

@Moeri अनावश्यक भविष्य को समाप्त करने और '? .' का उपयोग करके इसे छोटा बनाओ: 'सार्वजनिक स्थैतिक बूल IsAssignableToGenericType (इस प्रकार का प्रकार, सामान्य जेनेरिक) {वापसी नया [] {type} .Concat (type.GetTypeInfo()। कार्यान्वित इंटरफेस) .एनी (i => i.IsConstructedGenericType && iGetGenericTypeDefinition() == सामान्य) || (type.GetTypeInfo()। बेस टाइप? आईएसएसिग्नेबल टोजेनरिक टाइप (जेनेरिक) ?? झूठा); } एक 0 लाइनर के लिए ' – HappyNomad

4

महान जानकारी के लिए धन्यवाद। दृढ़ता के लिए, मैंने इसे एक विस्तार विधि में दोबारा प्रतिक्रिया दी है और इसे एक ही कथन में कम कर दिया है।

sometype:

public static bool IsAssignableToGenericType(this Type givenType, Type genericType) 
{ 
    return givenType.GetInterfaces().Any(t => t.IsGenericType && t.GetGenericTypeDefinition() == genericType) || 
     givenType.BaseType != null && (givenType.BaseType.IsGenericType && givenType.BaseType.GetGenericTypeDefinition() == genericType || 
             givenType.BaseType.IsAssignableToGenericType(genericType)); 
} 

अब यह आसानी से कहा जा सकता है।IsAssignableToGenericType (टाइपऑफ (MyGenericType <>))

+0

+1 –

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