【问题标题】:What's the point of indicating AllowMultiple=false on an abstract Attribute class?在抽象 Attribute 类上指示 AllowMultiple=false 有什么意义?
【发布时间】:2010-04-15 21:23:57
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: .net attributes allowmultiple


    【解决方案1】:

    在基本属性上设置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] 覆盖该设置。

    【讨论】:

    • 他的问题是当 AllowMultiple = false 时允许多个相同的 MyAttributeBase。所以你只能被允许一个 [Foo] OR [Bar]。
    • 正如提问者发现并在问题中明确指出的那样,在 .NET 中无法做到这一点。我正在解决“我们为什么要将 AttributeUsage 应用于基本属性?”的问题
    【解决方案2】:

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

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

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

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

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

    【讨论】:

    • 您的解释已经是问题的一部分。该提案需要代码的所有权。基本问题是关于继承的。
    • 已经有一个 AcceptVerbsAttribute 完全符合您的描述。正如@Henk 所指出的,我感兴趣的是基类属性的使用是如何发挥作用的。
    • 好的,抱歉 - 我误解了你的问题。
    【解决方案3】:

    让我们做一个简短的测试:

    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 个属性,无论它们的使用设置如何。所以开发者可以忽略继承属性的使用设置。

    【讨论】:

    • 我的问题不在于属性如何应用于方法,而是为什么基本属性的属性用法似乎并不重要。您实际上不能应用 ActionMethodSelectorAttribute,因为它是抽象的。你必须申请其中一个孩子。抽象基本属性上的 AllowMultiple=false 似乎没有什么区别,因为您可以应用基本属性的多个子属性,只要它们属于不同的派生类型。为了让您的示例应用属性用法,需要将其应用于 BaseAttribute。
    • @tvanfosson 现在我终于明白你的意思了。 AllowMultiple 仅指定相同最终属性类型的实例,而不是其基类。对于许多用途来说,它非常有意义。在这种特殊情况下,它是无用的,但仍然会指导您,例如,如果您将从 ActionMethodSelectorAttribute 制作自己的非抽象后代 - 类似于 [HttpMethod(HttpMethod.Post)]
    猜你喜欢
    • 2014-10-30
    • 2014-01-15
    • 1970-01-01
    • 2013-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-27
    • 2011-06-13
    相关资源
    最近更新 更多