Allow a custom Attribute only on specific type
Solution 1
No, you can't, basically. You can limit it to struct
vs class
vs interface
, that is about it. Plus: you can't add attributes to types outside your code anyway (except for via TypeDescriptor
, which isn't the same).
Solution 2
You can run this unit test to check it.
First, declare validation attribute 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;
}
}
Create unit test:
[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() + "'"))));
}
}
}
}
Your attribute, for example allow only decimal property types:
[AttributeUsage(AttributeTargets.Property)]
[PropertyType(typeof(decimal))]
public class PriceAttribute : Attribute
{
}
Example model:
public class TestModel
{
[Price]
public decimal Price1 { get; set; } // ok
[Price]
public double Price2 { get; set; } // error
}
Solution 3
You could write code yourself to enforce correct use of your attribute class, but that's as much as you can do.
Solution 4
The code below will return an error if the attribute was placed on a property/field that is not List of string.
The line if (!(value is List<string> list))
may be a C#6 or 7 feature.
[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"); ;
}
}
Related videos on Youtube
Comments
-
gdoron is supporting Monica almost 2 years
Is there a way to force the compiler to restrict the usage of a custom attribute to be used only on specific property types like int, short, string (all the primitive types)?
similar to the AttributeUsageAttribute's ValidOn-AttributeTargets enumeration.-
Admin over 12 yearsNo, this isn't possible. The most you could do is write a unit test that uses reflection and validates its usage. But nothing in the compiler will do this.
-
Marc Gravell over 12 yearsalso; you can't add attributes to classes outside your control anyway - so you can't add attributes to
int
orstring
. Do you mean "only to properties that areint
orstring
" ? if so, the answer is still "no" ;p -
gdoron is supporting Monica over 12 years@MarcGravell ofcourse I ment int, string properties and not changing the int class itself, But I'll edit. thanks for the answer.
-
Mike Nakis almost 11 yearsSome good, workable answers have been given on this duplicate which was asked just 15 days later: stackoverflow.com/questions/8574275/…
-
-
Marc Gravell over 12 yearsside note: an attribute has no access to it's own context, so any check here would have to be in the reflection code that queries for the attribute
-
Admin over 12 yearsI wrote a unit test (NUnit) once that used Cecil to verify my "allowable" attribute usages.
-
derHugo about 5 yearswhat is a
ValidationAttribute
? -
Ian about 5 yearsThis forces validation. works in conjunction with the ModelState
-
derHugo about 5 yearshm I don't have such a type (Unity 2019 with .Net 4.6)
-
Dominic Jonas almost 5 years@derHugo add
Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.ComponentModel.DataAnnotations.dll
and namespaceusing System.ComponentModel.DataAnnotations;