2011-03-23 21 views
79

के सभी विरासत में मिला कक्षाएं प्राप्त करें? (स्यूडोकोड :)मैं एक अमूर्त वर्ग है एक अमूर्त वर्ग

foreach (Implementation imp in Reflection.GetInheritedClasses(AbstractDataExport) 
{ 
    AbstractDataExport derivedClass = Implementation.CallConstructor(); 
    Console.WriteLine(derivedClass.name) 
} 

CsvExporter 
XmlExporter 

की तरह एक उत्पादन के साथ

?

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

यदि आपके पास वैकल्पिक समाधान हैं: एम बताएं।

धन्यवाद

उत्तर

116

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

public static class ReflectiveEnumerator 
{ 
    static ReflectiveEnumerator() { } 

    public static IEnumerable<T> GetEnumerableOfType<T>(params object[] constructorArgs) where T : class, IComparable<T> 
    { 
     List<T> objects = new List<T>(); 
     foreach (Type type in 
      Assembly.GetAssembly(typeof(T)).GetTypes() 
      .Where(myType => myType.IsClass && !myType.IsAbstract && myType.IsSubclassOf(typeof(T)))) 
     { 
      objects.Add((T)Activator.CreateInstance(type, constructorArgs)); 
     } 
     objects.Sort(); 
     return objects; 
    } 
} 

कुछ नोट:

  • इस आपरेशन के "लागत" के बारे में चिंता मत करो - आप केवल एक बार यह कर करने जा रहे हैं (उम्मीद) और फिर भी यह नहीं के रूप में धीमी है जैसा कि आप सोचेंगे।
  • आपको Assembly.GetAssembly(typeof(T)) का उपयोग करने की आवश्यकता है क्योंकि आपकी बेस क्लास एक अलग असेंबली में हो सकती है।
  • आपको type.IsClass और !type.IsAbstract मानदंडों का उपयोग करने की आवश्यकता है क्योंकि यदि आप इंटरफ़ेस या अमूर्त वर्ग को तुरंत चालू करने का प्रयास करते हैं तो यह अपवाद फेंक देगा।
  • मुझे IComparable को लागू करने के लिए गणना की गई कक्षाओं को मजबूर करना पसंद है ताकि उन्हें सॉर्ट किया जा सके।
  • आपके बच्चे वर्गों में समान कन्स्ट्रक्टर हस्ताक्षर होना चाहिए, अन्यथा यह एक अपवाद फेंक देगा। यह आमतौर पर मेरे लिए कोई समस्या नहीं है।
+1

एक प्रकार एक ही समय में अमूर्त और गैर-वर्ग नहीं हो सकता है? – user2341923

+1

यह बिल्कुल पागल है! मुझे नहीं पता था कि आप प्रतिबिंब के साथ इस तरह की चीजें कर सकते हैं। – CanadaIT

+0

@ user2341923 एक enum? – Cesar

10

यह सुंदर तरीका नहीं हो सकता है, लेकिन आप विधानसभा में सभी वर्गों पुनरावृति और हर एक के लिए Type.IsSubclassOf(AbstractDataExport) आह्वान कर सकते हैं।

+1

+1: मेरा मानना ​​है कि यह एकमात्र समाधान है। –

+0

बहुत बहुत धन्यवाद! मेरा समाधान आपके सुझावों पर आधारित है। – trampi

3

typeof(AbstractDataExport).Assembly आपको बताता है कि आपके प्रकार एक असेंबली में हैं (मानते हैं कि सभी समान हैं)।

assembly.GetTypes() आपको उस असेंबली में सभी प्रकार देता है या assembly.GetExportedTypes() आपको सार्वजनिक प्रकार देता है।

प्रकारों के माध्यम से इटरेटिंग और type.IsAssignableFrom() का उपयोग करके आपको यह पता चलता है कि प्रकार व्युत्पन्न है या नहीं।

+0

आपके उत्तर के लिए धन्यवाद, assembly.gettypes() मुझे समाधान के लिए कुछ चाहिए था। – trampi

34

मानते हुए वे सभी एक ही विधानसभा में परिभाषित कर रहे हैं, तो आप कर सकते हैं:

IEnumerable<AbstractDataExport> exporters = typeof(AbstractDataExport) 
    .Assembly.GetTypes() 
    .Where(t => t.IsSubclassOf(typeof(AbstractDataExport)) && !t.IsAbstract) 
    .Select(t => (AbstractDataExport)Activator.CreateInstance(t)); 
+1

धन्यवाद, बहुत दिलचस्प है। 'Activator.CreateInstance' – trampi

+3

पर एक नज़र डालने की आवश्यकता है 't.GetConstructor (Type.EmptyTypes)! = Null' को उन हिस्सों को तुरंत चालू करने की कोशिश करने के लिए अतिरिक्त बाधा के रूप में उपयोग करें जिनके पास पैरामीटर रहित कन्स्ट्रक्टर नहीं है। – mrexodia

0

खैर, प्रतिबिंब अपने दोस्त यहाँ है। इसे लागू करने से पहले, आप इसके प्रदर्शन पहलुओं पर विचार करना चाहेंगे - "प्रतिबिंब हमेशा महंगा होता है" :-)

+1

आपके उत्तर के लिए धन्यवाद! क्या आप मुझे अधिक जानकारी दे सकते हैं कि निष्पादन कितना धीमा होगा? तुलना करने के लिए कुछ भी? :) – trampi

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