2010-04-15 3 views
12

In einem aktuellen question über MVC-Attribute fragte jemand, ob die Verwendung von HttpPost- und HttpDelete-Attributen für eine Aktionsmethode dazu führen würde, dass entweder Anfragetyp erlaubt ist oder keine Anfragen erlaubt sind (da es nicht gleichzeitig Post und Delete sein kann) gleiche Zeit). Ich bemerkte, dass ActionMethodSelectorAttribute, von dem HttpPostAttribute und HttpDeleteAttribute beide herleiten mitWas bedeutet AllowMultiple = false für eine abstrakte Attributklasse?

[AttributeUsage(AttributeTargets.Method, 
       AllowMultiple = false, 
       Inherited = true)] 

verziert ich erwartet hatte es nicht zuzulassen, dass sowohl Httppost und HttpDelete auf dem gleichen Verfahren aus diesem Grund, aber der Compiler beschwert sich nicht. Meine begrenzten Tests zeigen mir, dass die Verwendung des Attributs in der Basisklasse einfach ignoriert wird. AllowMultiple lässt scheinbar nur zwei der selben Attribute auf eine Methode/Klasse zu und scheint nicht zu berücksichtigen, ob diese Attribute von der gleichen Klasse abgeleitet sind, die so konfiguriert ist, dass sie keine Vielfachen erlaubt. Darüber hinaus schließt die Verwendung des Attributs für die Basisklasse nicht einmal aus, dass Sie die Attributverwendung für eine abgeleitete Klasse ändern. Was ist der Grund dafür, die Werte für die Basisattributklasse zu setzen? Ist es nur beratend oder fehlt mir etwas Grundlegendes in ihrer Arbeitsweise?

FYI - es stellt sich heraus, dass beide grundsätzlich verhindert, dass diese Methode überhaupt in Betracht gezogen wird. Die Attribute werden unabhängig voneinander ausgewertet, und eine davon zeigt immer an, dass die Methode für die Anforderung nicht gültig ist, da sie nicht gleichzeitig Post und Delete sein kann.

Antwort

8

Einstellung AllowMultiple auf einem Basisattribut legt im Wesentlichen einen Standardwert für alle Attribute fest, die daraus abgeleitet werden. Wenn Sie möchten, dass alle Attribute, die von einem Basisattribut abgeleitet werden, mehrere Instanzen zulassen, können Sie die Duplizierung speichern, indem Sie das Attribut [AttributeUsage] auf das Basisattribut anwenden, sodass nicht alle abgeleiteten Attribute dasselbe tun müssen.

Beispiel: Angenommen, Sie dies zulassen wollte:

public abstract class MyAttributeBase : Attribute 
{ 
} 

public sealed class FooAttribute : MyAttributeBase 
{ 
} 

public sealed class BarAttribute : MyAttributeBase 
{ 
} 

[Foo] 
[Foo] 
[Bar] 
[Bar] 
public class A 
{ 
} 

Wie es aussieht, dies erzeugt einen Compiler-Fehler, da benutzerdefinierte Attribute, die standardmäßig nicht erlauben mehrere Instanzen. Nun könnte man ein [AttribteUsage] sowohl [Foo] und [Bar] wie dies gilt:

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] 
public sealed class FooAttribute : MyAttributeBase 
{ 
} 

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] 
public sealed class BarAttribute : MyAttributeBase 
{ 
} 

aber alternativ könnte man stattdessen wenden sie nur an die Basis Attribut:

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] 
public abstract class MyAttributeBase : Attribute 
{ 
} 

public sealed class FooAttribute : MyAttributeBase 
{ 
} 

public sealed class BarAttribute : MyAttributeBase 
{ 
} 

Beide Ansätze haben die gleiche unmittelbare Wirkung (mehrere Instanzen von sowohl [Foo] als auch [Bar] sind erlaubt), aber der zweite Ansatz hat auch den indirekten Effekt, dass andere Attribute, die von [MyAttribute] abgeleitet sind, jetzt mehrere Instanzen zulassen, es sei denn sie haben ihre eigenen [AttributeUsage], die diese Einstellung überschreibt.

+0

Sein Problem ist, dass mehrere derselben erlauben MyAttributeBase ist zulässig, wenn AllowMultiple = false ist. Du hättest also nur einen einzigen [Foo] OR [Bar]. – Rangoric

+0

Es gibt keine Möglichkeit, das in .NET zu tun, wie der Fragesteller entdeckt und in der Frage deutlich gemacht hat. Ich ging auf die Frage ein, "warum sollten wir AttributUsage jemals auf ein Basisattribut anwenden?" –

0

AllowMultiple erlaubt/verbietet, dass das spezifische Attribut mehr als einmal verwendet wird. Es hat keine Auswirkung darauf, ob andere Attribute damit kombiniert werden können.

So zum Beispiel, wenn Sie eine ObfuscationAttribute haben, der steuert, ob die Umbenennung des Verfahrens aktiviert oder deaktiviert ist, würden Sie nicht Nutzer wollen in der Lage sein, dies zu tun:

[Obfuscation("DisableRenaming")] 
[Obfuscation("EnableRenaming")] 
void MyMethod() 
{ 
} 

In diesem Fall Verschleierung kann nicht sein, Sowohl aktiviert als auch deaktiviert, verwenden Sie AllowMultiple = false, um sicherzustellen, dass die Methode nur einmal mit diesem bestimmten Attribut markiert wird.

Was Sie hypothetisch tun könnten, ist in Ihrem gegenseitig ausschließenden Fall ein einzelnes Attribut namens HttpSettings, das einen Paraneter verwendet, der angibt, ob es auf den Post- oder Delete- "Modus" angewendet wurde. Dies könnte dann AllowMultiple = false sein, um die gegenseitige Exklusivität der Optionen zu erzwingen.

+0

Ihre Erklärung ist bereits Teil der Frage. Der Vorschlag erfordert den Besitz des Codes. Die Basisfrage betrifft die Vererbung. –

+0

Es gibt bereits ein AcceptVerbsAttribute, das genau das tut, was Sie beschreiben. Wie @Henk angibt, interessiert mich, wie die Grundklassenattributnutzung ins Spiel kommt, wenn überhaupt. – tvanfosson

+0

Ok, tut mir leid - ich habe deine Frage falsch interpretiert. –

0

Lassen Sie uns einen kurzen Test machen:

using System; 
using System.Text; 
using System.Collections.Generic; 
using System.Linq; 
using Microsoft.VisualStudio.TestTools.UnitTesting; 
using System.Reflection; 

namespace TestAttrs { 
    public abstract class BaseAttribute : Attribute { 
     public string text; 
    } 

    [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)] 
    public class MultipleInheritedAttribute : BaseAttribute { } 

    [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)] 
    public class MultipleNonInheritedAttribute : BaseAttribute { } 

    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)] 
    public class SingleInheritedAttribute : BaseAttribute { } 

    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] 
    public class SingleNonInheritedAttribute : BaseAttribute { } 

    public class BaseClass { 
     [MultipleInherited(text = "MultipleInheritedBase")] 
     [MultipleNonInherited(text = "MultipleNonInheritedBase")] 
     [SingleInherited(text = "SingleInheritedBase")] 
     [SingleNonInherited(text = "SingleNonInheritedBase")] 
     public virtual void Method() { ; } 
    } 

    public class DerivedClass : BaseClass { 
     [MultipleInherited(text = "MultipleInheritedDerived")] 
     [MultipleNonInherited(text = "MultipleNonInheritedDerived")] 
     [SingleInherited(text = "SingleInheritedDerived")] 
     [SingleNonInherited(text = "SingleNonInheritedDerived")] 
     public override void Method() { 
      base.Method(); 
     } 
    } 

    [TestClass] 
    public class AttributesTest { 
     [TestMethod] 
     public void TestAttributes() { 
      MemberInfo mi = typeof(DerivedClass).GetMember("Method")[0]; 
      object[] attrs = mi.GetCustomAttributes(true); 

      string log = ""; 
      foreach(BaseAttribute attr in attrs) { 
       log += attr.text+"|"; 
      } 
      Assert.AreEqual("MultipleInheritedDerived|SingleInheritedDerived|SingleNonInheritedDerived|MultipleNonInheritedDerived|MultipleInheritedBase|", log); 
     } 
    } 
} 

Wie Sie sehen können, wenn Attribut gekennzeichnet ist Inherted=true dann wird es für abgeleitete Klassen zurückgegeben werden, aber wenn geerbte Methode mit demselben Attribut gekennzeichnet ist - es wird unterdrückt, wenn AllowMultiple=false. Also - in unserem Test enthält Protokollstring sowohl "MultipleInheritedDerived" und "MultipleInheritedBase", aber nicht "SingleInheritedBase".

So beantworten Sie Ihre Frage - was ist ein Punkt? Diese Kombination ermöglicht es Ihnen, einen Basis-Controller mit einer virtuellen Methode zu verwenden, die Sie überschreiben können, ohne sich über das Attribut Sorgen zu machen (es wird von der Basismethode übernommen), aber gleichzeitig, um es zu überschreiben, wenn Sie möchten. HttpPostAttribute ist kein gutes Beispiel, weil es keine Parameter hat, aber andere Attribute können von solchen Einstellungen profitieren.

Bitte beachten Sie, dass Coderaubend Attribute:

 object[] attrs = mi.GetCustomAttributes(true); 

gibt an, dass es in ererbten Attribute interessiert ist. Wenn schreiben

 object[] attrs = mi.GetCustomAttributes(false); 

dann Ergebnis würde 4 Attribute unabhängig von ihren Einstellungen enthalten. Es ist also möglich, dass der Entwickler die Einstellung für die Verwendung vererbten Attributs ignoriert.

+0

Mein Problem war nicht, wie Attribute auf Methoden angewendet werden, aber warum hat das Basisattribut eine Attributverwendung, die anscheinend keine Rolle spielt. Sie können ein ActionMethodSelectorAttribute nicht wirklich anwenden, da es abstrakt ist. Sie müssen eines seiner Kinder anwenden. AllowMultiple = false für das abstrakte Basisattribut scheint keinen Unterschied zu machen, da Sie mehrere untergeordnete Attribute des Basisattributs anwenden können, solange sie von verschiedenen abgeleiteten Typen stammen. Für das zu verwendende Beispiel müsste die Attributverwendung auf BaseAttribute angewendet werden. – tvanfosson

+0

@tvanfosson Jetzt sehe ich endlich was du meinst. AllowMultiple gibt nur Instanzen derselben endgültigen Attributtypen an, nicht seine Basisklasse. Für viele Verwendungszwecke macht es einen perfekten Sinn. In diesem speziellen Fall ist es nutzlos, aber wird Sie immer noch führen, wenn Sie zum Beispiel Ihren eigenen nicht-abstrakten Nachkomme von ActionMethodSelectorAttribute machen - etwas wie [HttpMethod (HttpMethod.Post)] –

Verwandte Themen