Какой смысл того, чтобы указать на AllowMultiple=false на абстрактном классе Атрибута?

По недавнему вопросу об атрибутах MVC кто-то спросил, приведет ли использование атрибутов HttpPost и HttpDelete на методе действия или к позволяемому типу запроса или ни к каким позволяемым запросам (так как это не может быть и Сообщение и Удаление одновременно). Я заметил, что ActionMethodSelectorAttribute, из которого происходят HttpPostAttribute и HttpDeleteAttribute оба, украшен

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

Я ожидал, что это не разрешит и HttpPost и HttpDelete на том же методе из-за этого, но компилятор не жалуется. Мое ограниченное тестирование говорит мне, что использование атрибута на базовом классе просто проигнорировано. AllowMultiple на вид только запрещает два из того же атрибута от того, чтобы быть примененным до метода/класса и, кажется, не рассматривает, происходят ли те атрибуты из того же класса, который настроен для не разрешения кратных чисел. Кроме того, использование атрибута на базовом классе даже не устраняет Ваш от изменения использования атрибута на производном классе. При этом, какой смысл того, чтобы даже установить значения на основном классе атрибута? Это - просто консультация, или я пропускаю что-то фундаментальное в том, как они работают?

К вашему сведению - оказывается, что использование обоих в основном устраняет тот метод от того, чтобы когда-нибудь быть рассмотренным. Атрибуты оценены независимо, и один из них будет всегда указывать, что метод не действителен для запроса, так как это не может одновременно быть и Сообщение и Удаление.

16
задан Community 23 May 2017 в 12:14
поделиться

3 ответа

Установка 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
{
}

Но в качестве альтернативы вы можете просто применить его к атрибуту base:

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

public sealed class FooAttribute : MyAttributeBase
{
}

public sealed class BarAttribute : MyAttributeBase
{
}

Оба подхода имеют одинаковый прямой эффект (разрешено несколько экземпляров как [Foo], так и [Bar])но второй подход также имеет косвенный эффект, что any другие атрибуты, производные от [MyAttribute], теперь будут допускать несколько экземпляров, если у них нет собственного [AttributeUsage], который переопределяет этот параметр.

12
ответ дан 30 November 2019 в 23:05
поделиться

AllowMultiple разрешает / запрещает использование этого конкретного атрибута более одного раза. Это не влияет на то, можно ли с ним комбинировать другие атрибуты.

Так, например, если у вас есть ObfuscationAttribute, который контролирует, включено или отключено переименование метода, вы не хотите, чтобы пользователи могли делать это:

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

В этом случае обфускация не может быть одновременно включена и отключена, поэтому вы должны использовать AllowMultiple = false, чтобы гарантировать, что метод только один раз отмечен этим конкретным атрибутом.

То, что вы могли бы гипотетически сделать в вашем взаимоисключающем случае, - это использовать единственный атрибут под названием HttpSettings, который принимает паранетер, указывающий, применяется ли он к «режиму» публикации или удаления. Тогда это может быть AllowMultiple = false, чтобы обеспечить взаимную исключительность опций.

0
ответ дан 30 November 2019 в 23:05
поделиться

Давайте проведем небольшой тест:

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 атрибута независимо от их настроек использования. Таким образом, разработчик может игнорировать настройку использования наследуемого атрибута.

0
ответ дан 30 November 2019 в 23:05
поделиться
Другие вопросы по тегам:

Похожие вопросы: