web-dev-qa-db-ja.com

特定のタイプでのみカスタム属性を許可する

Int、short、string(すべてのプリミティブ型)などの特定のproperty型でのみ使用されるカスタム属性の使用をコンパイラーに強制する方法はありますか?
AttributeUsageAttribute のValidOn - AttributeTargets 列挙と同様です。

いいえ、基本的にできません。 struct vs class vs interfaceに制限できます。さらに、とにかくコード外の型に属性を追加することはできません(via TypeDescriptorは例外です)。

27
Marc Gravell

この単体テストを実行して確認できます。

まず、検証属性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() + "'"))));
                }
            }
        }
    }

たとえば、属性は10進数のプロパティタイプのみを許可します。

 [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
}
13
Sel

自分でコードを記述して、属性クラスの正しい使用を強制することもできますが、それは可能な限りのことです。

4
Anders Tornblad

以下のコードは、文字列のリストではないプロパティ/フィールドに属性が配置された場合にエラーを返します。

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
Ian