Können Attribute in C # dynamisch hinzugefügt werden?

143

Ist es möglich, zur Laufzeit Attribute hinzuzufügen oder den Wert eines Attributs zur Laufzeit zu ändern?

Jon Turner
quelle

Antworten:

67

Attribute sind statische Metadaten. Baugruppen, Module, Typen, Elemente, Parameter und Rückgabewerte sind in C # keine erstklassigen Objekte (z. B. ist die System.TypeKlasse lediglich eine reflektierte Darstellung eines Typs). Sie können eine Instanz eines Attributs für einen Typ abrufen und die Eigenschaften ändern, wenn sie beschreibbar sind. Dies wirkt sich jedoch nicht auf das Attribut aus, wenn es auf den Typ angewendet wird.

Mark Cidade
quelle
68

Dies hängt wirklich davon ab, was genau Sie erreichen möchten.

Mit dem System.ComponentModel.TypeDescriptor- Material können Typen, Eigenschaften und Objektinstanzen Attribute hinzugefügt werden, und es besteht die Einschränkung, dass Sie es auch zum Abrufen dieser Eigenschaften verwenden müssen. Wenn Sie den Code schreiben, der diese Attribute verwendet, und Sie innerhalb dieser Einschränkungen leben können, würde ich es definitiv vorschlagen.

Soweit ich weiß, sind das PropertyGrid-Steuerelement und die Oberfläche des Visual Studio-Designs die einzigen Dinge in der BCL, die das TypeDescriptor-Material verbrauchen. Auf diese Weise tun sie ungefähr die Hälfte der Dinge, die sie wirklich tun müssen.

Alex Lyman
quelle
7
Tatsächlich werden die meisten Datenbindungszwecke verwendet TypeDescriptor- nicht nur PropertyGrid.
Marc Gravell
1
Eine Problemumgehung zum Hinzufügen von Eigenschaftsmetadatenattributen in einem Silverlight-Projekt (wo TypeDescriptorund wo TypeDescriptionProvidernicht implementiert?
Shimmy Weitzhandler
1
Es ist wichtig zu beachten, dass TypeDescriptor.GetAttributes () keine doppelten Attribute verarbeitet. Es wird nur der letzte Attributtyp ausgewählt. [Attr(1), Attr(2), Attr(3)]Nur Ex Attr(3)wird gefunden.
Ohmusama
11

Das kannst du nicht. Eine Problemumgehung könnte darin bestehen, zur Laufzeit eine abgeleitete Klasse zu generieren und das Attribut hinzuzufügen, obwohl dies wahrscheinlich ein wenig übertrieben ist.

petr k.
quelle
10

Um anders zu sein, habe ich einen Artikel gefunden, der auf Reflection verweist.

Hier ist der Link: http://www.codeproject.com/KB/cs/dotnetattributes.aspx . Sie sollten auch einige der Kommentare am Ende des Artikels lesen, da mögliche Ansätze diskutiert werden.

torial
quelle
10
Beachten Sie, dass Sie zur Laufzeit Attribute mit den Reflection.Emit-Klassen erstellen können, ABER Sie können sie an Klassen binden, die Sie mit dem Emit-Paket erstellt haben, und nicht an vorhandene.
Panos
Was für eine nutzlose Antwort =)) Wir alle kümmern uns hier um bestehende Klassen, nicht um dynamische.
Hoffnungslos
@Hopeless, können Sie eine Unterklasse YourClassin YourRuntimeClassWithAttributes.
Motes
@Motes nicht sicher, was du meinst, meine Klassen sind alle vorher definiert, das heißt, alle Basisklassen (die meine Klassen erben) sollten auch vorher definiert / bestimmt werden. Ich kann mir keine Möglichkeit vorstellen, sich auf etwas einzulassen, das mit Reflection.Emit dynamisch erstellt wurde.
Hoffnungslos
1
@Hopeless: Wenn Sie einer vorhandenen Klasse Attribute dynamisch hinzufügen möchten, können Sie YourClasssie zur Laufzeit in Unterklassen unterteilen und eine identische Klasse mit einem geringfügig anderen Namen generieren, der auch die gewünschten dynamisch erstellten Attribute enthält. Durch Polymorphismus kann der Code für die Typprüfung weiterhin identifiziert werden deine Grundklasse.
Motes
4

Nein, ist es nicht.

Attribute sind Metadaten und werden in binärer Form in der kompilierten Assembly gespeichert (deshalb können Sie auch nur einfache Typen in ihnen verwenden).

Thomas Danecker
quelle
3

Ich glaube nicht. Selbst wenn ich falsch liege, können Sie nur hoffen, sie einem ganzen Typ hinzuzufügen, niemals einer Instanz eines Typs.

Joel Coehoorn
quelle
22
TypeDescriptor.AddAttributes (Object, Attribute []) fügt der Zielkomponenteninstanz Attribute auf Klassenebene hinzu.
Peter Wone
3

Wenn Sie etwas benötigen, um dynamisch hinzugefügt werden zu können, sind c # -Attribute nicht der richtige Weg. Sehen Sie sich an, wie Sie die Daten in XML speichern. Ich habe kürzlich ein Projekt durchgeführt, das ich mit Attributen gestartet habe, aber schließlich zur Serialisierung mit XML übergegangen bin.

Darren Kopp
quelle
1
Vielleicht ist es kein schöner Weg, aber es ist die Art und Weise, wie viele andere Bibliotheken es verwenden. Um das Verhalten dieser Bibliotheken anzupassen, müssen wir mit Reflection spielen =)) wirklich ein Deadlock.
Hoffnungslos
3

Warum musst du? Attribute geben zusätzliche Informationen zur Reflexion, aber wenn Sie extern wissen, welche Eigenschaften Sie möchten, benötigen Sie sie nicht.

Sie können Metadaten relativ einfach extern in einer Datenbank oder einer Ressourcendatei speichern.

Keith
quelle
1
Beseitigung der Kesselplatte. Wäre es nicht praktisch, wenn eine Klasse Attribute automatisch basierend auf dem Code innerhalb der Klasse generieren könnte? Ich versuche, so etwas herauszufinden, um die Boilerplate in SQL CLR-Objekten zu reduzieren. Wäre in anderen Sprachen einfach ... siehe paulgraham.com/avg.html
Duncan Bayne
1

Ich habe mich mit System.ComponentModel.TypeDescriptor sehr bemüht, ohne Erfolg. Das bedeutet nicht, dass es nicht funktionieren kann, aber ich würde gerne Code dafür sehen.

Im Gegenzug wollte ich einige Attributwerte ändern. Ich habe 2 Funktionen ausgeführt, die für diesen Zweck gut funktionieren.

        // ************************************************************************
        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
quelle