在抽象的Attribute类上指示AllowMultiple = false有什么意义?

时间:2010-04-15 21:23:58

标签: .net attributes allowmultiple

在最近关于MVC属性的question中,有人询问在动作方法上使用HttpPost和HttpDelete属性是否会导致允许请求类型或者不允许任何请求(因为它不能同时是Post并同时删除)。我注意到ActionMethodSelectorAttribute,HttpPostAttribute和HttpDeleteAttribute都派生用它来装饰

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

由于这个原因,我原本以为它不允许在同一个方法上使用HttpPost和HttpDelete,但是编译器没有抱怨。我的有限测试告诉我,基类的属性用法被简单地忽略了。 AllowMultiple似乎只允许将两个相同的属性应用于方法/类,并且似乎不考虑这些属性是否来自配置为不允许多个的同一个类。此外,基类上的属性用法甚至不会阻止您更改派生类的属性用法。既然如此,甚至在基本属性类上设置值有什么意义呢?它只是建议性的还是我错过了它们如何工作的基本内容?

仅供参考 - 事实证明,使用两者基本上排除了这种方法的考虑。这些属性是独立评估的,其中一个属性总是表明该方法对请求无效,因为它不能同时是Post和Delete。

3 个答案:

答案 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个属性,无论其使用设置如何。因此,开发人员可以忽略继承属性使用设置。