Czy atrybuty można dodawać dynamicznie w C #?

143

Czy można dodawać atrybuty w czasie wykonywania lub zmieniać wartość atrybutu w czasie wykonywania?

Jon Turner
źródło

Odpowiedzi:

67

Atrybuty to statyczne metadane. Zestawy, moduły, typy, składowe, parametry i wartości zwracane nie są obiektami pierwszej klasy w języku C # (np. System.TypeKlasa jest jedynie odzwierciedleniem reprezentacji typu). Możesz pobrać wystąpienie atrybutu dla typu i zmienić właściwości, jeśli są one zapisywalne, ale nie wpłynie to na atrybut, ponieważ jest stosowany do typu.

Mark Cidade
źródło
68

To naprawdę zależy od tego, co dokładnie próbujesz osiągnąć.

Element System.ComponentModel.TypeDescriptor może być użyty do dodawania atrybutów do typów, właściwości i instancji obiektów, i ma ograniczenie, że musisz go również użyć do pobrania tych właściwości. Jeśli piszesz kod, który wykorzystuje te atrybuty i możesz żyć w ramach tych ograniczeń, zdecydowanie bym to zasugerował.

O ile wiem, formant PropertyGrid i powierzchnia projektowa studia wizualnego to jedyne rzeczy w BCL, które zużywają rzeczy TypeDescriptor. W rzeczywistości w ten sposób robią mniej więcej połowę rzeczy, które naprawdę muszą zrobić.

Alex Lyman
źródło
7
W rzeczywistości większość zastosowań wiązań danych TypeDescriptor- nie tylko PropertyGrid.
Marc Gravell
1
Jakiekolwiek obejście, aby dodać atrybuty metadanych właściwości w projekcie Silverlight (gdzie TypeDescriptori TypeDescriptionProvidernie są zaimplementowane?
Shimmy Weitzhandler,
1
Należy zauważyć, że TypeDescriptor.GetAttributes () nie obsługuje zduplikowanych atrybutów. Wybiera tylko ostatni typ atrybutu. Znaleziono [Attr(1), Attr(2), Attr(3)]tylko Ex Attr(3).
ohmusama
11

Nie możesz. Jednym obejściem może być wygenerowanie klasy pochodnej w czasie wykonywania i dodanie atrybutu, chociaż jest to prawdopodobnie trochę przesada.

Piotr K.
źródło
10

Cóż, żeby być innym, znalazłem artykuł, który odwołuje się do metody Reflection.Emit.

Oto link: http://www.codeproject.com/KB/cs/dotnetattributes.aspx , zajrzyj również do niektórych komentarzy na dole artykułu, ponieważ omówione są możliwe podejścia.

torialny
źródło
10
Zauważ, że możesz tworzyć atrybuty w czasie wykonywania za pomocą klas Reflection.Emit, ALE możesz powiązać je z klasami, które zbudowałeś za pomocą pakietu Emit, a nie z istniejącymi.
Panos,
co za bezużyteczna odpowiedź =)) wszystkim zależy nam na klasach istniejących, a nie dynamicznych.
Beznadziejny
@Hopeless, możesz podklasować YourClassdo YourRuntimeClassWithAttributes.
Motes
@Motes nie jestem pewien, co masz na myśli, wszystkie moje klasy są zdefiniowane wcześniej, co oznacza, że ​​wszystkie klasy bazowe (które dziedziczą moje klasy) również powinny być wcześniej zdefiniowane / określone. Nie przychodzi mi do głowy żaden sposób, aby to było związane z czymś dynamicznie tworzonym za pomocą Reflection.Emit.
Beznadziejny
1
@Hopeless, jeśli chcesz dynamicznie dodawać atrybuty do istniejącej klasy YourClass, możesz podklasować ją w czasie wykonywania i wygenerować identyczną klasę o nieco innej nazwie, która ma również żądane dynamicznie tworzone atrybuty, a polimorfizm pozwoli kodowi sprawdzającemu typ nadal identyfikować Twoja klasa bazowa.
Motes
4

Nie, nie jest.

Atrybuty są metadanymi i są przechowywane w postaci binarnej w skompilowanym zestawie (dlatego też można w nich używać tylko prostych typów).

Thomas Danecker
źródło
3

Nie wierzę w to. Nawet jeśli się mylę, najlepsze, na co możesz liczyć, to dodanie ich do całego Typu, a nigdy do wystąpienia Typu.

Joel Coehoorn
źródło
22
TypeDescriptor.AddAttributes (Object, Attribute []) dodaje atrybuty na poziomie klasy do instancji składnika docelowego.
Peter wygrał
3

Jeśli potrzebujesz czegoś do dynamicznego dodawania, atrybuty C # nie są odpowiednie. Przyjrzyj się przechowywaniu danych w formacie XML. Niedawno zrobiłem projekt, który zacząłem z atrybutami, ale ostatecznie przeszedłem do serializacji w / xml.

Darren Kopp
źródło
1
może nie jest to piękny sposób, ale jest to sposób, w jaki wiele innych bibliotek decyduje się używać i aby dostosować zachowania tych bibliotek, musimy bawić się refleksją =)) naprawdę impasem.
Beznadziejny
3

Dlaczego potrzebujesz? Atrybuty dostarczają dodatkowych informacji do refleksji, ale jeśli zewnętrznie wiesz, które właściwości chcesz, nie potrzebujesz ich.

Możesz stosunkowo łatwo przechowywać metadane na zewnątrz w bazie danych lub pliku zasobów.

Keith
źródło
1
Eliminacja kotła. Czy nie byłoby przydatne, gdybyś mógł mieć klasę automagicznie generującą atrybuty na podstawie kodu w klasie? Próbuję wymyślić coś takiego, aby zmniejszyć schemat w obiektach SQL CLR. Byłoby łatwo w innych językach ... patrz paulgraham.com/avg.html
Duncan Bayne
1

Bardzo się starałem z System.ComponentModel.TypeDescriptor bez powodzenia. To nie znaczy, że nie może działać, ale chciałbym zobaczyć kod.

Z drugiej strony chciałem zmienić niektóre wartości atrybutów. Zrobiłem 2 funkcje, które działają dobrze w tym celu.

        // ************************************************************************
        public static void SetObjectPropertyDescription(this Type typeOfObject, string propertyName,  string description)
        {
            PropertyDescriptor pd = TypeDescriptor.GetProperties(typeOfObject)[propertyName];
            var att = pd.Attributes[typeof(DescriptionAttribute)] as DescriptionAttribute;
            if (att != null)
            {
                var fieldDescription = att.GetType().GetField("description", BindingFlags.NonPublic | BindingFlags.Instance);
                if (fieldDescription != null)
                {
                    fieldDescription.SetValue(att, description);
                }
            }
        }

        // ************************************************************************
        public static void SetPropertyAttributReadOnly(this Type typeOfObject, string propertyName, bool isReadOnly)
        {
            PropertyDescriptor pd = TypeDescriptor.GetProperties(typeOfObject)[propertyName];
            var att = pd.Attributes[typeof(ReadOnlyAttribute)] as ReadOnlyAttribute;
            if (att != null)
            {
                var fieldDescription = att.GetType().GetField("isReadOnly", BindingFlags.NonPublic | BindingFlags.Instance);
                if (fieldDescription != null)
                {
                    fieldDescription.SetValue(att, isReadOnly);
                }
            }
        }
Eric Ouellet
źródło