2009-03-03 15 views
119

प्रश्न MSDN example पर आधारित प्रश्न।कस्टम क्लास विशेषता वाले सभी वर्गों को कैसे समझाते हैं?

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

+1

एमएसडीएन उदाहरण लिंक एक मृत लिंक है। – MadTigger

उत्तर

159

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

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

सहमत हैं, लेकिन इस मामले में हम इसे कैस्परऑन के समाधान के अनुसार घोषणात्मक रूप से कर सकते हैं। यह उपज का उपयोग करने में सक्षम होना अच्छा है, यह नहीं –

+8

मैं LINQ की तरह :) के लिए और भी बेहतर होता है। वास्तव में इसे प्यार करो। लेकिन यह .NET 3.5 पर निर्भरता लेता है, जो उपज वापसी नहीं करता है। इसके अलावा, LINQ अंततः उपज वापसी के रूप में एक ही चीज़ के लिए टूट जाता है। तो आपने क्या हासिल किया है? एक विशेष सी # वाक्यविन्यास, यह एक वरीयता है। –

+0

तो बजाय उपज का उपयोग कर अपनी खुद की प्रगणक ... –

80

ठीक है, आप सभी विधानसभाओं कि वर्तमान एप्लिकेशन डोमेन में लोड कर रहे हैं में सभी कक्षाओं के माध्यम से गणना करने के लिए होगा। ऐसा करने के लिए, आप GetAssemblies method को वर्तमान ऐप डोमेन के लिए AppDomain उदाहरण पर कॉल करेंगे।

वहां से, आप GetExportedTypes (यदि आप केवल सार्वजनिक प्रकार चाहते हैं) या GetTypes पर प्रत्येक Assembly पर असेंबली में मौजूद प्रकार प्राप्त करने के लिए कॉल करेंगे।

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

आप आप के लिए यह आसान बनाने के लिए 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>() }; 

ऊपर क्वेरी अपनी विशेषता लागू किया गया, विशेषता (विशेषताओं) यह करने के लिए आवंटित के कहने के साथ साथ आप प्रत्येक प्रकार मिल जाएगा।

नोट अगर आप अपने आवेदन डोमेन में लोड विधानसभाओं की एक बड़ी संख्या है, कि आपरेशन महंगा हो सकता है। तुम इतनी तरह, आपरेशन के समय को कम करने के Parallel 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

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

+0

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

+0

समाधान के लिए धन्यवाद, लेकिन मैं वास्तव में इस पूरे LINQ चीज़ का आदी नहीं हूं :-) – tomash

8

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

// 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. 
     } 
    } 
} 
18

अन्य उत्तर संदर्भ GetCustomAttributes:

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

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

मेरा मानना ​​है कि यह उचित समाधान है जो ढांचे के इच्छित तरीके का उपयोग करता है। –

1

का उपयोग कर Portable .NET limitations के मामले में का एक उदाहरण के रूप में इस एक जोड़ा जा रहा है, निम्नलिखित कोड काम करना चाहिए:

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; 
       } 
      } 
     } 
    } 
4

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

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

फिल्टर करने के लिए एक और तरीका है Assembly.ReferencedAssemblies उपयोग करने के लिए है। संभवतः यदि आप किसी विशिष्ट विशेषता वाले वर्ग चाहते हैं, और वह विशेषता किसी विशिष्ट असेंबली में परिभाषित की गई है, तो आप केवल उस असेंबली और अन्य असेंबली की परवाह करते हैं जो इसका संदर्भ देते हैं। मेरे परीक्षणों में यह 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) 
संबंधित मुद्दे