在最近关于MVC属性的question中,有人询问在动作方法上使用HttpPost和HttpDelete属性是否会导致允许请求类型或者不允许任何请求(因为它不能同时是Post并同时删除)。我注意到ActionMethodSelectorAttribute,HttpPostAttribute和HttpDeleteAttribute都派生用它来装饰
[AttributeUsage(AttributeTargets.Method,
AllowMultiple = false,
Inherited = true)]
由于这个原因,我原本以为它不允许在同一个方法上使用HttpPost和HttpDelete,但是编译器没有抱怨。我的有限测试告诉我,基类的属性用法被简单地忽略了。 AllowMultiple似乎只允许将两个相同的属性应用于方法/类,并且似乎不考虑这些属性是否来自配置为不允许多个的同一个类。此外,基类上的属性用法甚至不会阻止您更改派生类的属性用法。既然如此,甚至在基本属性类上设置值有什么意义呢?它只是建议性的还是我错过了它们如何工作的基本内容?
仅供参考 - 事实证明,使用两者基本上排除了这种方法的考虑。这些属性是独立评估的,其中一个属性总是表明该方法对请求无效,因为它不能同时是Post和Delete。
答案 0 :(得分:11)
在基本属性上设置AllowMultiple
实际上为从中派生的所有属性设置了默认值。如果您希望从基本属性派生的所有属性都允许多个实例,那么您可以通过将[AttributeUsage]
属性应用于此属性来保存重复,从而避免对所有派生属性执行相同操作。
例如,假设你想允许这个:
public abstract class MyAttributeBase : Attribute
{
}
public sealed class FooAttribute : MyAttributeBase
{
}
public sealed class BarAttribute : MyAttributeBase
{
}
[Foo]
[Foo]
[Bar]
[Bar]
public class A
{
}
目前,这会产生编译器错误,因为默认情况下,自定义属性不允许多个实例。现在,您可以将[AttribteUsage]
同时应用于[Foo]
和[Bar]
:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public sealed class FooAttribute : MyAttributeBase
{
}
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public sealed class BarAttribute : MyAttributeBase
{
}
但是,您也可以将其应用于基本属性:
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public abstract class MyAttributeBase : Attribute
{
}
public sealed class FooAttribute : MyAttributeBase
{
}
public sealed class BarAttribute : MyAttributeBase
{
}
两种方法都具有相同的直接效果(允许[Foo]
和[Bar]
的多个实例)但第二种方法也具有任何其他属性派生的间接效果从[MyAttribute]
起,现在将允许多个实例,除非他们有自己的[AttributeUsage]
覆盖该设置。
答案 1 :(得分:0)
AllowMultiple允许/禁止多次使用该特定属性。它对其他属性是否可以与它结合没有影响。
因此,例如,如果您有一个ObfuscationAttribute来控制是启用还是禁用方法的重命名,那么您不希望用户能够执行此操作:
[Obfuscation("DisableRenaming")]
[Obfuscation("EnableRenaming")]
void MyMethod()
{
}
在这种情况下,无法同时启用和禁用混淆,因此您必须使用AllowMultiple = false来确保该方法仅使用此特定属性标记一次。
在你的互相排斥的情况下,你可以假设做的是使用一个名为HttpSettings的属性,它使用了一个指示是否应用于Post或Delete“mode”的paraneter。然后可以使用AllowMultiple = false来强制选项的互斥性。
答案 2 :(得分:0)
让我们做一个简短的测试:
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);
}
}
}
正如您所看到的,如果属性标记为Inherted=true
,那么它将针对派生类返回,但如果继承的方法标记有相同的属性 - 如果AllowMultiple=false
则会被抑制。
所以 - 在我们的测试中,日志字符串包含“MultipleInheritedDerived”和“MultipleInheritedBase”,但不包含“SingleInheritedBase”。
回答你的问题 - 有什么意义?这种组合允许你有一个虚拟方法的基本控制器,你可以覆盖它而不用担心属性(它将从基本方法中获取),但同时如果你想要能够覆盖它。 HttpPostAttribute不是一个很好的例子,因为它没有参数,但其他属性可以从这些设置中受益。
另外,请注意代码消耗属性:
object[] attrs = mi.GetCustomAttributes(true);
指定它对继承的属性感兴趣。如果写
object[] attrs = mi.GetCustomAttributes(false);
然后结果将包含4个属性,无论其使用设置如何。因此,开发人员可以忽略继承属性使用设置。