Какой смысл указывать AllowMultiple = false для абстрактного класса Attribute? - PullRequest
16 голосов
/ 16 апреля 2010

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

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

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

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

Ответы [ 3 ]

11 голосов
/ 29 апреля 2010

Установка 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], которые переопределяют этот параметр.

0 голосов
/ 23 апреля 2010

Давайте сделаем короткий тест:

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 голосов
/ 16 апреля 2010

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...