Pytanie na podstawie przykładu MSDN .
Powiedzmy, że mamy kilka klas C # z HelpAttribute w samodzielnej aplikacji klasycznej. Czy można wyliczyć wszystkie klasy z takim atrybutem? Czy ma sens rozpoznawanie zajęć w ten sposób? Własny atrybut zostałby użyty do wylistowania możliwych opcji menu, wybranie pozycji spowoduje wyświetlenie na ekranie instancji takiej klasy. Liczba klas / przedmiotów będzie rosła powoli, ale myślę, że w ten sposób unikniemy wyliczania ich wszystkich gdzie indziej.
Select
metodę rozszerzającą, a kompilator wygeneruje maszynę stanu, tak jakbyś wywołał jąSelect
z powodu użyciayield return
. Wreszcie, wszelkie zyski wydajności, które mogą być uzyskane w większości przypadków być mikro optymalizacje.Cóż, należałoby wyliczyć wszystkie klasy we wszystkich zestawach, które są ładowane do bieżącej domeny aplikacji. Aby to zrobić, należy wywołać
GetAssemblies
metodę wAppDomain
instancji dla bieżącej domeny aplikacji.Stamtąd można wywołać
GetExportedTypes
(jeśli chcesz tylko typy publiczne) lubGetTypes
na każdymAssembly
z nich, aby uzyskać typy zawarte w zestawie.Następnie możesz wywołać
GetCustomAttributes
metodę rozszerzenia dla każdejType
instancji, przekazując typ atrybutu, który chcesz znaleźć.Możesz użyć LINQ, aby uprościć to dla siebie:
Powyższe zapytanie dostarczy Ci każdy typ z zastosowanym do niego atrybutem, wraz z wystąpieniem przypisanego (-ych) atrybutu (-ów).
Zwróć uwagę, że jeśli masz dużą liczbę zestawów załadowanych do domeny aplikacji, ta operacja może być kosztowna. Możesz użyć Parallel LINQ, aby skrócić czas operacji, na przykład:
Filtrowanie według konkretnego
Assembly
jest proste:A jeśli zestaw zawiera dużą liczbę typów, możesz ponownie użyć Parallel LINQ:
źródło
Inne odpowiedzi odnoszą się do GetCustomAttributes . Dodanie tego jako przykładu użycia IsDefined
źródło
Jak już wspomniano, właściwą drogą jest refleksja. Jeśli zamierzasz często to wywoływać, zdecydowanie sugeruję buforowanie wyników, ponieważ refleksja, szczególnie wyliczanie w każdej klasie, może być dość powolne.
To jest fragment mojego kodu, który przebiega przez wszystkie typy we wszystkich załadowanych zestawach:
źródło
Jest to poprawa wydajności w stosunku do przyjętego rozwiązania. Iterowanie, chociaż wszystkie klasy mogą być powolne, ponieważ jest ich tak wiele. Czasami można odfiltrować cały zespół bez patrzenia na którykolwiek z jego typów.
Na przykład, jeśli szukasz atrybutu, który sam zadeklarowałeś, nie oczekujesz, że żadna z systemowych bibliotek DLL będzie zawierała typy z tym atrybutem. Właściwość Assembly.GlobalAssemblyCache to szybki sposób na sprawdzenie systemowych bibliotek DLL. Kiedy wypróbowałem to na prawdziwym programie, stwierdziłem, że mogę pominąć 30,101 typów i muszę sprawdzić tylko 1983 typy.
Innym sposobem filtrowania jest użycie Assembly.ReferencedAssemblies. Prawdopodobnie jeśli chcesz klas z określonym atrybutem, a ten atrybut jest zdefiniowany w określonym zestawie, to zależy Ci tylko na tym zestawie i innych zestawach, które się do niego odwołują. W moich testach pomogło to nieco bardziej niż sprawdzenie właściwości GlobalAssemblyCache.
Połączyłem oba i uzyskałem to jeszcze szybciej. Poniższy kod zawiera oba filtry.
źródło
W przypadku ograniczeń Portable .NET powinien działać następujący kod:
lub dla dużej liczby zestawów wykorzystujących stan pętli
yield return
:źródło
Możemy poprawić odpowiedź Andrew i przekonwertować całość na jedno zapytanie LINQ.
źródło