Разрешить пользовательский атрибут только для определенного типа - PullRequest
33 голосов
/ 05 декабря 2011

Есть ли способ заставить компилятор ограничить использование пользовательского атрибута, который будет использоваться только для определенных свойств типов, таких как int, short, string (все примитивные типы)?
аналогично перечислению AttributeUsageAttribute ValidOn- AttributeTargets .

Ответы [ 4 ]

27 голосов
/ 05 декабря 2011

Нет, вы не можете, в основном. Вы можете ограничить его до struct против class против interface, вот и все. Плюс: вы все равно не можете добавлять атрибуты к типам вне вашего кода (кроме как через TypeDescriptor, что не совпадает).

12 голосов
/ 29 ноября 2016

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

Сначала объявите атрибут валидации PropertyType:

  [AttributeUsage(AttributeTargets.Class)]
    // [JetBrains.Annotations.BaseTypeRequired(typeof(Attribute))] uncomment if you use JetBrains.Annotations
    public class PropertyTypeAttribute : Attribute
    {
        public Type[] Types { get; private set; }

        public PropertyTypeAttribute(params Type[] types)
        {
            Types = types;
        }
    }

Создать юнит-тест:

 [TestClass]
    public class TestPropertyType 
    {
        public static Type GetNullableUnderlying(Type nullableType)
        {
            return Nullable.GetUnderlyingType(nullableType) ?? nullableType;
        }

        [TestMethod]
        public void Test_PropertyType()
        {
            var allTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes());
            var allPropertyInfos = allTypes.SelectMany(a => a.GetProperties()).ToArray();

            foreach (var propertyInfo in allPropertyInfos)
            {
                var propertyType = GetNullableUnderlying(propertyInfo.PropertyType);
                foreach (var attribute in propertyInfo.GetCustomAttributes(true))
                {
                    var attributes = attribute.GetType().GetCustomAttributes(true).OfType<PropertyTypeAttribute>();
                    foreach (var propertyTypeAttr in attributes)
                        if (!propertyTypeAttr.Types.Contains(propertyType))
                            throw new Exception(string.Format(
                                "Property '{0}.{1}' has invalid type: '{2}'. Allowed types for attribute '{3}': {4}",
                                propertyInfo.DeclaringType,
                                propertyInfo.Name,
                                propertyInfo.PropertyType,
                                attribute.GetType(),
                                string.Join(",", propertyTypeAttr.Types.Select(x => "'" + x.ToString() + "'"))));
                }
            }
        }
    }

Ваш атрибут, например, разрешает только десятичные типы свойств:

 [AttributeUsage(AttributeTargets.Property)]
    [PropertyType(typeof(decimal))]
    public class PriceAttribute : Attribute
    {

    }

Пример модели:

public class TestModel  
{
    [Price]
    public decimal Price1 { get; set; } // ok

    [Price]
    public double Price2 { get; set; } // error
}
4 голосов
/ 23 марта 2018

Код ниже вернет ошибку, если атрибут был помещен в свойство / поле, которое не является списком строк.

Строка if (!(value is List<string> list)) может быть функцией C # 6 или 7.

[AttributeUsage(AttributeTargets.Property |
                AttributeTargets.Field, AllowMultiple = false)]
public sealed class RequiredStringListAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext context)
    {
        if (!(value is List<string> list))
            return new ValidationResult($"The required attrribute must be of type List<string>");

        bool valid = false;
        foreach (var item in list)
        {
            if (!string.IsNullOrWhiteSpace(item))
                valid = true;
        }

        return valid
            ? ValidationResult.Success
            : new ValidationResult($"This field is required"); ;
    }

}
4 голосов
/ 05 декабря 2011

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

...