Atrybuty i refleksja
Część sekcji Programowanie obiektowe ścieżki C# w Coddy. Lekcja 44 z 70.
Atrybuty to znaczniki metadanych, które dołączasz do elementów kodu, takich jak klasy, metody czy właściwości. Nie zmieniają bezpośrednio sposobu wykonywania kodu, ale dostarczają informacji, które można odczytać w czasie działania programu. Refleksja to mechanizm odczytujący te metadane.
Atrybut stosujesz, umieszczając go w nawiasach kwadratowych nad elementem docelowym:
[Obsolete("Use NewMethod instead")]
public void OldMethod() { }
[Serializable]
public class Person
{
public string Name { get; set; }
}Możesz tworzyć niestandardowe atrybuty, dziedzicząc po Attribute:
public class AuthorAttribute : Attribute
{
public string Name { get; }
public AuthorAttribute(string name) => Name = name;
}
[Author("John")]
public class Calculator { }Refleksja pozwala sprawdzać typy i odczytywać atrybuty w czasie wykonywania programu za pomocą przestrzeni nazw System.Reflection:
Type type = typeof(Calculator);
var attr = type.GetCustomAttribute<AuthorAttribute>();
Console.WriteLine(attr.Name); // JohnMożesz także odkrywać metody i właściwości oraz wywoływać je dynamicznie:
MethodInfo[] methods = type.GetMethods();
foreach (var method in methods)
Console.WriteLine(method.Name);Atrybuty i refleksja są wykorzystywane przez wiele frameworków — biblioteki do serializacji używają ich do kontrolowania danych wyjściowych JSON, frameworki testowe do wyszukiwania metod testowych, a kontenery wstrzykiwania zależności do automatycznego łączenia usług.
Wyzwanie
ŁatwyStwórzmy system wykrywania wtyczek, który używa atrybutów niestandardowych i refleksji, aby automatycznie znajdować klasy oznaczone jako wtyczki i opisywać je. Działa to podobnie do sposobu, w jaki rzeczywiste frameworki wykrywają komponenty, metody testowe lub punkty końcowe API w czasie działania programu.
Rozmieść kod w trzech plikach:
PluginAttribute.cs: Utwórz atrybut niestandardowy o nazwiePluginAttributew przestrzeni nazwPluginSystem. Twój atrybut powinien:- Dziedziczyć po
Attribute - Mieć dwie właściwości tylko do odczytu:
Name(string) iVersion(string) - Przyjmować obie wartości przez konstruktor
- Dziedziczyć po
Plugins.cs: Utwórz kilka klas wtyczek w przestrzeni nazwPluginSystem. Niektóre powinny być ozdobione atrybutemPluginAttribute, a jedna nie:ImageProcessor— oznaczona atrybutem[Plugin("Image Processor", "1.0")]DataExporter— oznaczona atrybutem[Plugin("Data Exporter", "2.5")]HelperUtility— NIE oznaczaj żadnym atrybutem (to zwykła klasa)
Program.cs: W głównym pliku użyj refleksji, aby wykryć wszystkie klasy, które mają atrybutPluginAttribute. Dla każdej wykrytej wtyczki wypisz jej informacje. Musisz:- Pobrać wszystkie typy z bieżącego zestawu
- Sprawdzić każdy typ pod kątem atrybutu
PluginAttribute - Pobrać i wyświetlić właściwości atrybutu
NameiVersion
Użyj System.Reflection, aby sprawdzać typy i pobierać atrybuty niestandardowe. Metoda GetCustomAttribute<T>() zwraca null, jeśli atrybut nie jest obecny, więc możesz użyć jej do filtrowania klas będących wtyczkami.
Wypisz każdą wykrytą wtyczkę w tym formacie, po jednej w każdym wierszu:
Plugin: {Name} (v{Version})Wtyczki powinny być wypisane w następującej kolejności: najpierw ImageProcessor, a następnie DataExporter. Klasa HelperUtility nie powinna pojawić się w wyniku, ponieważ nie ma tego atrybutu.
Oczekiwany wynik:
Plugin: Image Processor (v1.0)
Plugin: Data Exporter (v2.5)To wyzwanie pokazuje, jak frameworki używają atrybutów jako znaczników oraz refleksji do ich wykrywania — ta sama technika jest stosowana w kontenerach wstrzykiwania zależności, bibliotekach serializacji i frameworkach testowych!
Spróbuj swoich sił
using System;
using System.Reflection;
using PluginSystem;
class Program
{
public static void Main(string[] args)
{
// TODO: Pobierz wszystkie typy z bieżącego zestawu
// Wskazówka: Użyj Assembly.GetExecutingAssembly().GetTypes()
// TODO: Przejdź przez każdy typ i sprawdź, czy ma atrybut PluginAttribute
// Wskazówka: Użyj type.GetCustomAttribute<PluginAttribute>()
// TODO: Jeśli atrybut istnieje, wypisz informacje o wtyczce w formacie:
// Plugin: {Name} (v{Version})
}
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrznePrzestrzenie nazw i dyrektywyWprowadzenie do klas i obiektówSłowo kluczowe „this”Metody i parametryPola a właściwościKonstruktoryInicjalizatory obiektówPodsumowanie — prosty kalkulator4Dziedziczenie
Podstawowa składnia dziedziczenia (:)Słowo kluczowe 'base'Słowa kluczowe virtual i overrideKlasy sealedBazowa klasa 'object'Powtórzenie – hierarchia pracowników7Zaawansowane funkcje
Przeciążanie operatorówIndeksery (this[])Nadpisywanie ToString()Metody rozszerzającePodsumowanie – własna lista2Właściwości i składowe statyczne
Właściwości automatyczneWłaściwości tylko do odczytu i zapisuPola i metody statyczneKlasy statyczneSkładowe z ciałem wyrażeniowym5Polimorfizm i interfejsy
Polimorfizm czasu kompilacji a czasu wykonaniaInterfejs a klasa abstrakcyjnaWiele interfejsówJawne implementowanie interfejsówRzutowanie w górę i w dółPowtórka — kalkulator kształtów8Zaawansowane koncepcje OOP
Kompozycja zamiast dziedziczeniaTypy generyczne (klasy i metody)Delegaty i zdarzeniaAtrybuty i refleksjaIDisposable i instrukcja usingPodstawy wstrzykiwania zależności11Wzorce projektowe, część 2
Wzorzec poleceniaWzorzec adapteraWzorzec dekoratoraWzorzec metody szablonowejWzorzec stanuWzorzec kompozytu3Architektura klas
Dane instancji a dane statyczneSłowa kluczowe 'readonly' i 'const'Pola zapasowePowtórzenie — menedżer kont bankowych6Hermetyzacja
Modyfikatory dostępuWłaściwości w hermetyzacjiImplementacja ukrywania danychWzorce niezmiennościPowtórka — dane studentów9Argumenty zmiennej liczby
Słowo kluczowe „params”Parametry opcjonalneArgumenty nazwanePrzeciążanie metodPoćwicz samodzielnie: Kompilator C# online