Kann ich ein C # -Attribut mit einem Array oder einer anderen variablen Anzahl von Argumenten initialisieren?

104

Ist es möglich, ein Attribut zu erstellen, das mit einer variablen Anzahl von Argumenten initialisiert werden kann?

Beispielsweise:

[MyCustomAttribute(new int[3,4,5])]  // this doesn't work
public MyClass ...
Crono
quelle
11
Sie haben nur eine falsche Syntax für das Array. Es sollte "new int [] {3,4,5}" sein.
David Wengier

Antworten:

178

Attribute nehmen ein Array an. Wenn Sie das Attribut steuern, können Sie paramsstattdessen auch Folgendes verwenden (was für Verbraucher besser ist, IMO):

class MyCustomAttribute : Attribute {
    public int[] Values { get; set; }

    public MyCustomAttribute(params int[] values) {
       this.Values = values;
    }
}

[MyCustomAttribute(3, 4, 5)]
class MyClass { }

Ihre Syntax für die Array-Erstellung ist zufällig deaktiviert:

class MyCustomAttribute : Attribute {
    public int[] Values { get; set; }

    public MyCustomAttribute(int[] values) {
        this.Values = values;
    }
}

[MyCustomAttribute(new int[] { 3, 4, 5 })]
class MyClass { }
Mark Brackett
quelle
33

Sie können es tun, aber es ist nicht CLS-konform:

[assembly: CLSCompliant(true)]

class Foo : Attribute
{
    public Foo(string[] vals) { }
}
[Foo(new string[] {"abc","def"})]
static void Bar() {}

Zeigt an:

Warning 1   Arrays as attribute arguments is not CLS-compliant

Für die regelmäßige Verwendung von Reflexionen kann es vorzuziehen sein, mehrere Attribute zu haben, d. H.

[Foo("abc"), Foo("def")]

Dies funktioniert jedoch nicht mit TypeDescriptor/ PropertyDescriptor, wo nur eine einzige Instanz eines Attributs unterstützt wird (entweder der erste oder der letzte Gewinn, ich kann mich nicht erinnern, welcher).

Marc Gravell
quelle
3
Hinweis: Für mehrere Attribute ist ein AttributeUsage-Attribut für Ihr Attribut erforderlich. stackoverflow.com/questions/553540/…
russau
23

Versuchen Sie, den Konstruktor wie folgt zu deklarieren:

public class MyCustomAttribute : Attribute
{
    public MyCustomAttribute(params int[] t)
    {
    }
}

Dann können Sie es wie folgt verwenden:

[MyCustomAttribute(3, 4, 5)]

Scott Dorman
quelle
12

Das sollte okay sein. Aus der Spezifikation, Abschnitt 17.2:

Ein Ausdruck E ist ein Attribut-Argument-Ausdruck, wenn alle folgenden Aussagen wahr sind:

  • Der Typ E ist ein Attributparametertyp (§17.1.3).
  • Zur Kompilierungszeit kann der Wert von E in einen der folgenden Werte aufgelöst werden:
    • Ein konstanter Wert.
    • Ein System.Type-Objekt.
    • Ein eindimensionales Array von Attribut-Argument-Ausdrücken .

Hier ist ein Beispiel:

using System;

[AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)]
public class SampleAttribute : Attribute
{
    public SampleAttribute(int[] foo)
    {
    }
}

[Sample(new int[]{1, 3, 5})]
class Test
{
}
Jon Skeet
quelle
5
Achten Sie jedoch auf die CLS-Konformität
Marc Gravell
4

Ja, aber Sie müssen das übergebene Array initialisieren. Hier ist ein Beispiel aus einem Zeilentest in unseren Komponententests, bei dem eine variable Anzahl von Befehlszeilenoptionen getestet wird.

[Row( new[] { "-l", "/port:13102", "-lfsw" } )]
public void MyTest( string[] args ) { //... }
Rob Prouse
quelle
2

Du kannst das tun. Ein anderes Beispiel könnte sein:

class MyAttribute: Attribute
{
    public MyAttribute(params object[] args)
    {
    }
}

[MyAttribute("hello", 2, 3.14f)]
class Program
{
    static void Main(string[] args)
    {
    }
}
Alan
quelle
1

Um auf Marc Gravells Antwort zurückzugreifen, können Sie ein Attribut mit Array-Parametern definieren, aber das Anwenden eines Attributs mit einem Array-Parameter ist nicht CLS-kompatibel. Das Definieren eines Attributs mit einer Array-Eigenschaft ist jedoch perfekt CLS-kompatibel.

Mir wurde klar, dass Json.NET, eine CLS-kompatible Bibliothek, eine Attributklasse JsonPropertyAttribute mit einer Eigenschaft namens ItemConverterParameters hat, die ein Array von Objekten ist.

TBrink
quelle