कैसे कस्टम वर्ग विशेषता के साथ सभी वर्गों की गणना?


151

MSDN उदाहरण पर आधारित प्रश्न ।

मान लें कि हमारे पास स्टैंडअलोन डेस्कटॉप एप्लिकेशन में HelpAttribute के साथ कुछ C # कक्षाएं हैं। क्या इस तरह की विशेषता के साथ सभी वर्गों की गणना करना संभव है? क्या यह इस तरह से वर्गों को पहचानने के लिए समझ में आता है? संभव मेनू विकल्पों को सूचीबद्ध करने के लिए कस्टम विशेषता का उपयोग किया जाएगा, आइटम का चयन ऐसे वर्ग के स्क्रीन उदाहरण के लिए लाएगा। कक्षाओं / वस्तुओं की संख्या धीरे-धीरे बढ़ेगी, लेकिन इस तरह से हम उन सभी को कहीं और गणना करने से बच सकते हैं, मुझे लगता है।

जवाबों:


205

हाँ बिल्कुल। प्रतिबिंब का उपयोग करना:

static IEnumerable<Type> GetTypesWithHelpAttribute(Assembly assembly) {
    foreach(Type type in assembly.GetTypes()) {
        if (type.GetCustomAttributes(typeof(HelpAttribute), true).Length > 0) {
            yield return type;
        }
    }
}

7
सहमत हैं, लेकिन इस मामले में हम इसे कैस्परऑन के समाधान के अनुसार घोषित कर सकते हैं। उपज का उपयोग करने में सक्षम होना अच्छा है, यह भी अच्छा नहीं है :)
जॉन स्कीट

9
मुझे LINQ पसंद है। इसे प्यार करो, वास्तव में। लेकिन यह .NET 3.5 पर निर्भरता लेता है, जो रिटर्न नहीं देता है। इसके अलावा, LINQ अंततः उपज रिटर्न के रूप में अनिवार्य रूप से एक ही चीज के लिए टूट जाती है। तो आपने क्या हासिल किया? एक विशेष C # वाक्यविन्यास, वह एक प्राथमिकता है।
एंड्रयू अरनोट

1
@AndrewArnott सबसे कम और कोड की सबसे छोटी लाइनें प्रदर्शन के लिए अप्रासंगिक हैं, वे केवल पठनीयता और स्थिरता के लिए संभव योगदानकर्ता हैं। मैं इस कथन को चुनौती देता हूं कि वे सबसे कम वस्तुओं को आवंटित करते हैं और प्रदर्शन तेजी से होगा (विशेषकर अनुभवजन्य प्रमाण के बिना); आपने मूल रूप से Selectएक्सटेंशन विधि लिखी है , और कंपाइलर एक राज्य मशीन उत्पन्न करेगा जैसे कि यदि आप Selectअपने उपयोग के कारण कहते हैं yield return। अंत में, मामलों के बहुमत में प्राप्त किया जा सकता है कि किसी भी प्रदर्शन लाभ सूक्ष्म अनुकूलन हो।
कैस्परऑन

1
काफी सही, @casperOne। एक बहुत ही मामूली अंतर, विशेष रूप से प्रतिबिंब के वजन के साथ तुलना में। शायद एक परिपूर्ण ट्रेस में कभी नहीं आएगा।
एंड्रयू अरनोट

1
बेशक रेस्परर का कहना है कि "फ़ॉरच लूप को LINQ अभिव्यक्ति में परिवर्तित किया जा सकता है" जो इस तरह दिखता है: असेंबली ।GetTypes ()। जहाँ (टाइप => type.GetCustomAttributes (टाइपोफ़ (हेल्पअवार्ड), ट्रू)) 0)।
डेविड बैरो

107

ठीक है, आपको उन सभी असेंबली में सभी वर्गों के माध्यम से गणना करना होगा जो वर्तमान ऐप डोमेन में लोड किए गए हैं। ऐसा करने के लिए, आप मौजूदा ऐप डोमेन के लिए GetAssembliesविधि पर कॉल करेंगे AppDomain

वहां से, आप विधानसभा में निहित प्रकारों को प्राप्त करने के लिए GetExportedTypes(यदि आप केवल सार्वजनिक प्रकार चाहते हैं) या GetTypesप्रत्येक पर कॉल करेंगे Assembly

फिर, आप प्रत्येक उदाहरण पर GetCustomAttributesएक्सटेंशन विधि को कॉल करेंगे Type, जिस प्रकार की विशेषता को आप ढूंढना चाहते हैं।

आप इसे सरल बनाने के लिए LINQ का उपयोग कर सकते हैं:

var typesWithMyAttribute =
    from a in AppDomain.CurrentDomain.GetAssemblies()
    from t in a.GetTypes()
    let attributes = t.GetCustomAttributes(typeof(HelpAttribute), true)
    where attributes != null && attributes.Length > 0
    select new { Type = t, Attributes = attributes.Cast<HelpAttribute>() };

उपरोक्त क्वेरी आपको उस पर लागू अपनी विशेषता के साथ प्रत्येक प्रकार के साथ मिल जाएगी, साथ ही उसे सौंपे गए विशेषता (एस) के उदाहरण के साथ।

ध्यान दें कि यदि आपके पास बड़ी संख्या में असेंबली आपके एप्लिकेशन डोमेन में लोड हैं, तो यह ऑपरेशन महंगा हो सकता है। आप ऑपरेशन के समय को कम करने के लिए समानांतर LINQ का उपयोग कर सकते हैं , जैसे:

var typesWithMyAttribute =
    // Note the AsParallel here, this will parallelize everything after.
    from a in AppDomain.CurrentDomain.GetAssemblies().AsParallel()
    from t in a.GetTypes()
    let attributes = t.GetCustomAttributes(typeof(HelpAttribute), true)
    where attributes != null && attributes.Length > 0
    select new { Type = t, Attributes = attributes.Cast<HelpAttribute>() };

विशिष्ट पर इसे छानना Assemblyसरल है:

Assembly assembly = ...;

var typesWithMyAttribute =
    from t in assembly.GetTypes()
    let attributes = t.GetCustomAttributes(typeof(HelpAttribute), true)
    where attributes != null && attributes.Length > 0
    select new { Type = t, Attributes = attributes.Cast<HelpAttribute>() };

और अगर असेंबली में बड़ी संख्या में प्रकार हैं, तो आप फिर से समानांतर LINQ का उपयोग कर सकते हैं:

Assembly assembly = ...;

var typesWithMyAttribute =
    // Partition on the type list initially.
    from t in assembly.GetTypes().AsParallel()
    let attributes = t.GetCustomAttributes(typeof(HelpAttribute), true)
    where attributes != null && attributes.Length > 0
    select new { Type = t, Attributes = attributes.Cast<HelpAttribute>() };

1
सभी भरी हुई विधानसभाओं में सभी प्रकार की गणना बस बहुत धीमी होगी और आपको अधिक लाभ नहीं देगी। यह संभावित रूप से एक सुरक्षा जोखिम भी है। आप शायद अनुमान लगा सकते हैं कि किन विधानसभाओं में आपकी रुचि के प्रकार शामिल होंगे।
एंड्रयू अर्नाट

@ और अरनोट: सही, लेकिन यह वही है जो पूछा गया था। किसी विशेष असेंबली के लिए क्वेरी को नीचे prune करना काफी आसान है। यह भी आप प्रकार और विशेषता के बीच मानचित्रण देने का अतिरिक्त लाभ है।
कैस्पर ओने

1
आप System.Reflection.Assembly.GetExecutingAssembly () के साथ सिर्फ मौजूदा असेंबली पर एक ही कोड का उपयोग कर सकते हैं
क्रिस मोशिनि

@ChrisMoschini हां, आप कर सकते हैं, लेकिन आप हमेशा वर्तमान विधानसभा को स्कैन नहीं करना चाहते हैं। इसे खुला छोड़ना बेहतर है।
कैस्परऑन

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

34

अन्य उत्तर संदर्भ GetCustomAttributes । इस एक को इसडाईफाइंड के उपयोग के उदाहरण के रूप में जोड़ना

Assembly assembly = ...
var typesWithHelpAttribute = 
        from type in assembly.GetTypes()
        where type.IsDefined(typeof(HelpAttribute), false)
        select type;

3
मेरा मानना ​​है कि यह उचित समाधान है जो फ्रेमवर्क का उपयोग करता है।
एलेक्सी ओमेलेंको

11

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

यह मेरे कोड का एक टुकड़ा है जो सभी भरी हुई विधानसभाओं में सभी प्रकारों से चलता है:

// this is making the assumption that all assemblies we need are already loaded.
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) 
{
    foreach (Type type in assembly.GetTypes())
    {
        var attribs = type.GetCustomAttributes(typeof(MyCustomAttribute), false);
        if (attribs != null && attribs.Length > 0)
        {
            // add to a cache.
        }
    }
}

9

यह स्वीकृत समाधान के शीर्ष पर एक प्रदर्शन वृद्धि है। Iterating हालांकि सभी कक्षाएं धीमी हो सकती हैं क्योंकि बहुत सारे हैं। कभी-कभी आप इसके किसी भी प्रकार को देखे बिना पूरी विधानसभा को फ़िल्टर कर सकते हैं।

उदाहरण के लिए यदि आप एक ऐसी विशेषता की तलाश कर रहे हैं जिसे आपने खुद घोषित किया है, तो आप उस विशेषता के साथ किसी भी प्रकार के सिस्टम DLL को शामिल करने की अपेक्षा नहीं करते हैं। असेंबली .GlobalAssemblyCache संपत्ति सिस्टम DLL के लिए जाँच करने का एक त्वरित तरीका है। जब मैंने इसे एक वास्तविक कार्यक्रम पर आज़माया तो मैंने पाया कि मैं 30,101 प्रकारों को छोड़ सकता हूँ और मुझे केवल 1,983 प्रकारों की जाँच करनी होगी।

फ़िल्टर करने का दूसरा तरीका असेंबली का उपयोग करना है। संभवतः यदि आप एक विशिष्ट विशेषता वाली कक्षाएं चाहते हैं, और उस विशेषता को एक विशिष्ट असेंबली में परिभाषित किया गया है, तो आप केवल उस विधानसभा और अन्य विधानसभाओं के बारे में परवाह करते हैं जो इसे संदर्भित करते हैं। मेरे परीक्षणों में इस GlobalAssemblyCache संपत्ति की जाँच करने से थोड़ा अधिक मदद मिली।

मैंने इन दोनों को मिला दिया और इसे और भी तेज कर दिया। नीचे दिए गए कोड में दोनों फ़िल्टर शामिल हैं।

        string definedIn = typeof(XmlDecoderAttribute).Assembly.GetName().Name;
        foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
            // Note that we have to call GetName().Name.  Just GetName() will not work.  The following
            // if statement never ran when I tried to compare the results of GetName().
            if ((!assembly.GlobalAssemblyCache) && ((assembly.GetName().Name == definedIn) || assembly.GetReferencedAssemblies().Any(a => a.Name == definedIn)))
                foreach (Type type in assembly.GetTypes())
                    if (type.GetCustomAttributes(typeof(XmlDecoderAttribute), true).Length > 0)

4

पोर्टेबल .NET सीमाओं के मामले में , निम्न कोड काम करना चाहिए:

    public static IEnumerable<TypeInfo> GetAtributedTypes( Assembly[] assemblies, 
                                                           Type attributeType )
    {
        var typesAttributed =
            from assembly in assemblies
            from type in assembly.DefinedTypes
            where type.IsDefined(attributeType, false)
            select type;
        return typesAttributed;
    }

या लूप-स्टेट आधारित का उपयोग करके बड़ी संख्या में विधानसभाओं के लिए yield return:

    public static IEnumerable<TypeInfo> GetAtributedTypes( Assembly[] assemblies, 
                                                           Type attributeType )
    {
        foreach (var assembly in assemblies)
        {
            foreach (var typeInfo in assembly.DefinedTypes)
            {
                if (typeInfo.IsDefined(attributeType, false))
                {
                    yield return typeInfo;
                }
            }
        }
    }

0

हम एंड्रयू के जवाब में सुधार कर सकते हैं और पूरी चीज़ को एक LINQ क्वेरी में बदल सकते हैं।

    public static IEnumerable<Type> GetTypesWithHelpAttribute(Assembly assembly)
    {
        return assembly.GetTypes().Where(type => type.GetCustomAttributes(typeof(HelpAttribute), true).Length > 0);
    }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.