2008-08-25 7 views
416

प्रतिबिंब का उपयोग करके, मैं कम से कम कोड के साथ सी # 3.0/.NET 3.5 के साथ इंटरफ़ेस को कार्यान्वित करने वाले सभी प्रकार कैसे प्राप्त कर सकता हूं, और पुनरावृत्तियों को कम कर सकता हूं? सभी लोड विधानसभाओं के माध्यम सेइंटरफ़ेस को लागू करने वाले सभी प्रकारों को प्राप्त करना

foreach (Type t in this.GetType().Assembly.GetTypes()) 
    if (t is IMyInterface) 
     ; //do stuff 
+0

क्या उदाहरण कोड काम करता है?मुझे आपकी स्थिति के साथ झूठी नकारात्मकता मिली है। –

+2

ऊपर दिए गए कोड में यदि कथन हमेशा गलत होगा क्योंकि आप परीक्षण कर रहे हैं कि टाइप क्लास (टी) का एक उदाहरण आपके इंटरफ़ेस को लागू करता है, जो तब तक नहीं होगा जब तक कि टाइप आईएमआईइंटरफेस (जिस स्थिति में यह हमेशा सत्य रहे)। – Liazy

उत्तर

617

खान में सी # 3.0 :)

var type = typeof(IMyInterface); 
var types = AppDomain.CurrentDomain.GetAssemblies() 
    .SelectMany(s => s.GetTypes()) 
    .Where(p => type.IsAssignableFrom(p)); 

असल में, पुनरावृत्तियों की कम से कम राशि हमेशा रहेंगे यह होगा:

loop assemblies 
loop types 
    see if implemented. 
+138

ध्यान दें कि सूची में इंटरफ़ेस भी शामिल हो सकता है। अंतिम पंक्ति को '.Where (p => type.IsAssignableFrom (p) &&! P.IsInterface) में बदलें; 'इसे फ़िल्टर करने के लिए (या' p.IsClass')। – jtpereyda

+23

नोट: यह उत्तर गलत है !, यह "असाइनमेंट संगतता" जांचता है कि इंटरफ़ेस लागू नहीं किया गया है या नहीं। उदाहरण के लिए 'सूची ' 'IENumerable ' लागू नहीं करता है, लेकिन यह विधि वास्तव में गलत है जो covariance के कारण .NET 4.0 में सच हो जाएगी। [सही उत्तर यहां है] (http://stackoverflow.com/a/12602220/2530848) –

+9

@ श्रीराम सक्थिवेल पहले बंद, जेनेरिक मान निर्दिष्ट नहीं किए गए थे। दूसरा, यह प्रश्न पूर्व-तारीखों को कॉन्वर्सिस है। तीसरा, आप यह धारणा करते हैं कि कॉन्वर्सेंट रिटर्न कुछ ऐसा नहीं है जो वे चाहते हैं। –

11

पाश, पाश उनके सभी प्रकार के माध्यम से, और अगर वे इंटरफ़ेस को लागू की जाँच करें:

यह है कि मैं क्या फिर से लिखना चाहते हैं।

कुछ की तरह:

Type ti = typeof(IYourInterface); 
foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies()) { 
    foreach (Type t in asm.GetTypes()) { 
     if (ti.IsAssignableFrom(t)) { 
      // here's your type in t 
     } 
    } 
} 
4

संपादित करें: मैंने यह स्पष्ट करने के लिए संपादन को देखा है कि मूल प्रश्न पुनरावृत्तियों/कोड में कमी के लिए था और यह सब स्वागत है एल और एक अभ्यास के रूप में अच्छा है, लेकिन असली दुनिया की परिस्थितियों में आप सबसे तेज़ कार्यान्वयन चाहते हैं, भले ही अंतर्निहित LINQ दिखता है।

भारित प्रकारों के माध्यम से पुन: प्रयास करने के लिए यहां मेरी उपयोग विधि है। यह नियमित कक्षाओं के साथ-साथ इंटरफेस को भी संभालता है, और बहिष्कार टाइप सिस्टम विकल्प को बहुत अधिक गति देता है यदि आप अपने/थर्ड-पार्टी कोडबेस में कार्यान्वयन की तलाश में हैं।

public static List<Type> GetSubclassesOf(this Type type, bool excludeSystemTypes) { 
    List<Type> list = new List<Type>(); 
    IEnumerator enumerator = Thread.GetDomain().GetAssemblies().GetEnumerator(); 
    while (enumerator.MoveNext()) { 
     try { 
      Type[] types = ((Assembly) enumerator.Current).GetTypes(); 
      if (!excludeSystemTypes || (excludeSystemTypes && !((Assembly) enumerator.Current).FullName.StartsWith("System."))) { 
       IEnumerator enumerator2 = types.GetEnumerator(); 
       while (enumerator2.MoveNext()) { 
        Type current = (Type) enumerator2.Current; 
        if (type.IsInterface) { 
         if (current.GetInterface(type.FullName) != null) { 
          list.Add(current); 
         } 
        } else if (current.IsSubclassOf(type)) { 
         list.Add(current); 
        } 
       } 
      } 
     } catch { 
     } 
    } 
    return list; 
} 

यह सुंदर नहीं है, मैं स्वीकार करूंगा।

+1

गणक IDISposable लागू करते हैं जिसे एक कोशिश/अंत में निपटाया नहीं जा रहा है। एक foreach या linq का उपयोग करना बेहतर है। – TamusJRoyce

2

ऐसा करने के लिए कोई आसान तरीका नहीं है (प्रदर्शन के संदर्भ में) जो आप करना चाहते हैं।

प्रतिबिंब मुख्य रूप से असेंबली और प्रकारों के साथ काम करता है ताकि आपको असेंबली के सभी प्रकार मिलें और सही इंटरफेस के लिए पूछें।

Assembly asm = Assembly.Load("MyAssembly"); 
Type[] types = asm.GetTypes(); 
Type[] result = types.where(x => x.GetInterface("IMyInterface") != null); 

है कि आप सभी प्रकार कि MyAssembly विधानसभा में IMyInterface लागू

-3

आप कुछ LINQ का उपयोग सूची प्राप्त करने के सकता है मिल जाएगा: यहाँ एक उदाहरण है

var types = from type in this.GetType().Assembly.GetTypes() 
      where type is ISomeInterface 
      select type; 

लेकिन वास्तव में, क्या वह और अधिक पठनीय है?

var results = from type in someAssembly.GetTypes() 
       where typeof(IFoo).IsAssignableFrom(type) 
       select type; 

ध्यान दें कि रयान Rinaldi के सुझाव गलत था:

+5

यदि यह काम करता है तो यह अधिक पठनीय हो सकता है। दुर्भाग्यवश, आपका क्लॉज यह देखने के लिए जांच कर रहा है कि सिस्टम का एक उदाहरण है या नहीं। टाइप क्लास ISomeInterface लागू करता है, जो कभी भी सत्य नहीं होगा, जब तक कि ISomeInterface वास्तव में IReflect या ICustomAttributeProvider नहीं है, इस मामले में यह हमेशा सत्य होगा। –

+0

ऊपर कार्ल नायक का उत्तर कहां से खंड को सही करने का उत्तर है: IsAssignableFrom। एक उत्तर के लिए आसान गलती। – TamusJRoyce

49

एक सभा में सभी प्रकार कि IFoo इंटरफ़ेस को लागू लगाने के लिए। यह 0 प्रकार वापस आ जाएगा। आप

where type is IFoo 

नहीं लिख सकते हैं क्योंकि प्रकार एक System.Type उदाहरण है, और प्रकार IFoo की कभी नहीं होगा। इसके बजाय, आप यह देखने के लिए जांचते हैं कि IFoo प्रकार से असाइन करने योग्य है या नहीं। इससे आपके अपेक्षित परिणाम मिलेंगे।

इसके अलावा, एडम राइट के सुझाव, जिसे वर्तमान में उत्तर के रूप में चिह्नित किया गया है, भी गलत है, और इसी कारण से। रनटाइम पर, आप देखेंगे कि 0 प्रकार वापस आते हैं, क्योंकि सभी सिस्टम। टाइप उदाहरण IFoo कार्यान्वयन नहीं थे।

6

यह मेरे लिए काम किया (यदि आप चाहें तो आप देखने में प्रणाली प्रकारों को छोड़ सकता है):

Type lookupType = typeof (IMenuItem); 
IEnumerable<Type> lookupTypes = GetType().Assembly.GetTypes().Where(
     t => lookupType.IsAssignableFrom(t) && !t.IsInterface); 
16

अन्य जवाब यहां IsAssignableFrom का उपयोग करें। here वर्णित अनुसार, आप System नामस्थान से FindInterfaces का भी उपयोग कर सकते हैं।

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

static void Main() { 
    const string qualifiedInterfaceName = "Interfaces.IMyInterface"; 
    var interfaceFilter = new TypeFilter(InterfaceFilter); 
    var path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); 
    var di = new DirectoryInfo(path); 
    foreach (var file in di.GetFiles("*.dll")) { 
     try { 
      var nextAssembly = Assembly.ReflectionOnlyLoadFrom(file.FullName); 
      foreach (var type in nextAssembly.GetTypes()) { 
       var myInterfaces = type.FindInterfaces(interfaceFilter, qualifiedInterfaceName); 
       if (myInterfaces.Length > 0) { 
        // This class implements the interface 
       } 
      } 
     } catch (BadImageFormatException) { 
      // Not a .net assembly - ignore 
     } 
    } 
} 

public static bool InterfaceFilter(Type typeObj, Object criteriaObj) { 
    return typeObj.ToString() == criteriaObj.ToString(); 
} 

यदि आप एक से अधिक मिलान करना चाहते हैं तो आप इंटरफेस की एक सूची सेट अप कर सकते हैं।

+0

यह एक स्ट्रिंग इंटरफेस नाम की तलाश में है जो मैं खोज रहा था। – senthil

+0

किसी भिन्न डोमेन में असेंबली लोड करते समय काम करता है, क्योंकि प्रकार को स्ट्रिंग में क्रमबद्ध किया जाना चाहिए। बहुत अच्छा! – TamusJRoyce

40

यह मेरे लिए काम किया। यह लूप हालांकि वर्गों और चेक को देखने के लिए अगर वे myInterface

foreach (Type mytype in System.Reflection.Assembly.GetExecutingAssembly().GetTypes() 
       .Where(mytype => mytype .GetInterfaces().Contains(typeof(myInterface)))) { 
    //do stuff 
} 
+1

आप मानते हैं कि असेंबली मुख्य निष्पादन योग्य है। एक अतिरिक्त परियोजना नहीं है। आप पुनरावृत्तियों का एक गुच्छा हालांकि अनावश्यक रूप से पुनरावृत्त कर रहे हैं। फ्रेमवर्क भारी भारोत्तोलन करना बेहतर है। फिर मिलने पर आगे नीचे फ़िल्टर करें। यदि प्रासंगिक हो, तो कृपया अपना उत्तर अपडेट करें। सूची तर्क शामिल करें। var classTypesImplementingInterface = AppDomain.CurrentDomain.GetAssemblies()। SelectMany (x => x.GetTypes())। (mytype => typeof (myInterface) .sAssignableFrom (mytype) और&type.GetInterfaces() में शामिल हैं (टाइपऑफ (myInterface))); foreach (वस्तुओं में var आइटम) Console.Log (item.Name); – TamusJRoyce

35

से derrived कर रहे हैं मुझे खुशी है कि यह एक बहुत पुरानी सवाल है, लेकिन मैं मैं तारीख को सभी प्रश्नों के उत्तर के रूप में भविष्य के उपयोगकर्ताओं के लिए एक और उत्तर जोड़ना होगा कुछ फार्म का उपयोग सोचा Assembly.GetTypes का।

जबकि GetTypes() वास्तव में सभी प्रकारों को वापस कर देगा, इसका मतलब यह नहीं है कि आप उन्हें सक्रिय कर सकते हैं और इस प्रकार संभावित रूप से ReflectionTypeLoadException फेंक सकते हैं।

एक प्रकार होगा जब प्रकार लौटाया base लेकिन base से derivedderived की है कि से एक अलग विधानसभा में परिभाषित किया जाता है सक्रिय करने में सक्षम नहीं होने के लिए एक उत्कृष्ट उदाहरण, एक विधानसभा कि बुला विधानसभा को संदर्भित नहीं करती।

तो कहते हैं कि हमने: जो AssemblyC में है

Class A // in AssemblyA 
Class B : Class A, IMyInterface // in AssemblyB 
Class C // in AssemblyC which references AssemblyB but not AssemblyA 

तो ClassC में हम बाद में हामी भर जवाब के अनुसार कुछ करना:

var type = typeof(IMyInterface); 
var types = AppDomain.CurrentDomain.GetAssemblies() 
    .SelectMany(s => s.GetTypes()) 
    .Where(p => type.IsAssignableFrom(p)); 

तो यह एक ReflectionTypeLoadException फेंक देते हैं।

इसका कारण यह है AssemblyC में AssemblyA के लिए एक संदर्भ के बिना आप के लिए सक्षम नहीं होगा:

var bType = typeof(ClassB); 
var bClass = (ClassB)Activator.CreateInstance(bType); 

दूसरे शब्दों ClassB में लोड करने योग्य जो कुछ है कि GetTypes जांच के कॉल और पर फेंकता है नहीं है।

तो सुरक्षित रूप से इस Phil Haacked लेख Get All Types in an Assembly के अनुसार तो लोड करने योग्य प्रकार के लिए परिणाम सेट अर्हता और Jon Skeet code को आप के बजाय की तरह कुछ करना होगा:

public static class TypeLoaderExtensions { 
    public static IEnumerable<Type> GetLoadableTypes(this Assembly assembly) { 
     if (assembly == null) throw new ArgumentNullException("assembly"); 
     try { 
      return assembly.GetTypes(); 
     } catch (ReflectionTypeLoadException e) { 
      return e.Types.Where(t => t != null); 
     } 
    } 
} 

और फिर:

private IEnumerable<Type> GetTypesWithInterface(Assembly asm) { 
    var it = typeof (IMyInterface); 
    return asm.GetLoadableTypes().Where(it.IsAssignableFrom).ToList(); 
} 
+3

इससे मुझे एक सुपर अजीब समस्या से निपटने में मदद मिली, जहां मेरी टेस्ट प्रोजेक्ट में GetTypes विफल हो जाएंगे और केवल हमारे सीआई-पर्यावरण में। GetLoadableTypes इस समाधान के लिए एक फिक्स था। त्रुटि स्थानीय वातावरण में पुन: उत्पन्न नहीं होगी और यह यह था: System.Reflection.ReflectionTypeLoadException: अनुरोधित प्रकारों में से एक या अधिक लोड करने में असमर्थ। अधिक जानकारी के लिए लोडरएक्सेप्शंस संपत्ति पुनर्प्राप्त करें। अधिक विशेष रूप से यह शिकायत कर रहा था कि एक ऐसा प्रकार था जिसमें ठोस कार्यान्वयन नहीं था और यह इकाई परीक्षण परियोजना में हुआ था। इसके लिए धन्यवाद! –

+1

इस उत्तर समाधान के रूप में चिह्नित किया जाना चाहिए, यह आज मेरे गधे को बचाया है, क्योंकि जैसे @Lari Tuomisto ने कहा, स्थानीय env पर हम फिर से उत्पाद नहीं कर सकता है इसी तरह की त्रुटि – Lightning3

+1

मामले में यह किसी और में मदद करता है: इस समाधान मेरे लिए काम किया है, लेकिन मैं सूची से इंटरफ़ेस प्रकार दूर करने के लिए इसे संशोधित करना पड़ा। मैं 'CreateInstance' उन सभी के लिए सक्रिय करने के लिए चाहते थे, और एक अपवाद उत्पन्न हुआ था जब यह वास्तविक इंटरफेस (जो मुझे थोड़ी देर के लिए उलझन में था जब मैंने सोचा कि वास्तविक इंटरफ़ेस इस समाधान में रास्ते से हट गया था) बनाने के लिए कोशिश कर रहा था। इसलिए मैं 'के लिए कोड बदल GetLoadableTypes (विधानसभा) .Where (interfaceType.IsAssignableFrom) .Where (टी => (t.Equals (interfaceType))!) ToList();।'। –

0

मैं अपवाद मिला linq-code में, इसलिए मैं इसे इस तरह से करता हूं (एक जटिल विस्तार के बिना):

private static IList<Type> loadAllTypes(Types[] interfaces) 
{ 
    IList<Type> objects = new List<Type>(); 

    // find all types 
    foreach (var interfaceType in interfaces) 
     foreach (var currentAsm in AppDomain.CurrentDomain.GetAssemblies()) 
      try 
      { 
       foreach (var currentType in currentAsm.GetTypes()) 
        if (interfaceType.IsAssignableFrom(currentType) && currentType.IsClass && !currentType.IsAbstract) 
         objects.Add(currentType); 
      } 
      catch { } 

    return objects; 
}