18

在最近关于 MVC 属性的一个问题上,有人询问在操作方法上使用 HttpPost 和 HttpDelete 属性是否会导致请求类型被允许或不允许请求(因为它不能同时是 Post 和 Delete )。我注意到 HttpPostAttribute 和 HttpDeleteAttribute 都从中派生的 ActionMethodSelectorAttribute 装饰有

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

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

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

4

3 回答 3

14

对基本属性进行设置AllowMultiple实质上为从它派生的所有属性设置了默认值。如果您希望从基本属性派生的所有属性都允许多个实例,那么您可以通过[AttributeUsage]将此效果应用到基本属性的属性来节省重复,而无需对所有派生属性执行相同操作。

例如,假设您想允许这样做:

public abstract class MyAttributeBase : Attribute
{
}

public sealed class FooAttribute : MyAttributeBase
{
}

public sealed class BarAttribute : MyAttributeBase
{
}

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

就目前而言,这会产生编译器错误,因为默认情况下自定义属性不允许多个实例。现在,您可以将 an[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]覆盖该设置的实例。

于 2010-04-29T13:47:05.993 回答
0

AllowMultiple 允许/禁止该特定属性被多次使用。它对其他属性是否可以与之组合没有影响。

例如,如果你有一个 ObfuscationAttribute 来控制方法的重命名是启用还是禁用,你不希望用户能够这样做:

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

在这种情况下,混淆不能同时启用和禁用,因此您将使用 AllowMultiple=false 来确保该方法仅使用此特定属性标记一次。

在相互排斥的情况下,您可以假设做的是使用一个名为 HttpSettings 的属性,该属性带有一个参数,指示它是应用于 Post 还是 Delete“模式”。这可以是 AllowMultiple=false 以强制执行选项的互斥性。

于 2010-04-15T21:41:02.657 回答
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);

那么 result 将包含 4 个属性,无论它们的使用设置如何。因此开发人员可以忽略继承属性使用设置。

于 2010-04-23T00:43:03.647 回答