C# 仅允许特定类型上的自定义属性
是否有办法强制编译器限制自定义属性的使用,使其仅用于特定的属性类型,如int、short、string(所有基本类型)?C# 仅允许特定类型上的自定义属性,c#,.net,compiler-construction,attributes,C#,.net,Compiler Construction,Attributes,是否有办法强制编译器限制自定义属性的使用,使其仅用于特定的属性类型,如int、short、string(所有基本类型)? 与ValidOn枚举类似。不,基本上不能。您可以将其限制为structvsclassvsinterface,就是这样。另外:无论如何,您都不能向代码之外的类型添加属性(除了通过不同的TypeDescriptor。您可以自己编写代码来强制正确使用属性类,但这就是您所能做的。您可以运行此单元测试来检查它 首先,声明验证属性PropertyType: [AttributeUsa
与ValidOn枚举类似。不,基本上不能。您可以将其限制为
struct
vsclass
vsinterface
,就是这样。另外:无论如何,您都不能向代码之外的类型添加属性(除了通过不同的TypeDescriptor
。您可以自己编写代码来强制正确使用属性类,但这就是您所能做的。您可以运行此单元测试来检查它
首先,声明验证属性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() + "'"))));
}
}
}
}
示例模型:
public class TestModel
{
[Price]
public decimal Price1 { get; set; } // ok
[Price]
public double Price2 { get; set; } // error
}
如果属性放置在非字符串列表的属性/字段上,下面的代码将返回错误 如果(!(值为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"); ;
}
}
[AttributeUsage(AttributeTargets.Property|
AttributeTargets.Field,AllowMultiple=false)]
公共密封类RequiredStringListAttribute:ValidationAttribute
{
受保护的覆盖ValidationResult有效(对象值、ValidationContext上下文)
{
如果(!(值为列表))
返回新的ValidationResult($“所需属性必须是List类型”);
bool valid=false;
foreach(列表中的变量项)
{
如果(!string.IsNullOrWhiteSpace(项))
有效=真;
}
返回有效
?验证结果。成功
:new ValidationResult($“此字段为必填项”);
}
}
不,这是不可能的。您所能做的最多就是编写一个使用反射并验证其使用情况的单元测试。但是编译器中的任何东西都不能做到这一点;无论如何,您不能将属性添加到您无法控制的类中-因此您不能将属性添加到int
或string
。您的意思是“仅适用于int
或string
”的属性吗?如果是,答案仍然是“否”;p@MarcGravell当然是int元素,字符串属性,不改变int类本身,但我会编辑。谢谢你的回答。关于这个副本,我们给出了一些很好的、可行的答案,这个副本在15天后才被问到:旁注:一个属性无法访问它自己的上下文,所以这里的任何检查都必须在查询该属性的反射代码中。我曾经编写了一个单元测试(NUnit),使用Cecil来验证我的“允许”属性用法。什么是ValidationAttribute
?这将强制验证。与ModelStateM一起工作我没有这样的类型(Unity 2019 with.Net 4.6)@derHugo add程序集位置:C:\Windows\Microsoft.Net\Framework\v4.0.30319\System.ComponentModel.DataAnnotations.dll
和命名空间使用System.ComponentModel.DataAnnotations代码>
[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"); ;
}
}