命名参数类型约束
我正在设计一个自定义属性类。
public class MyAttr: Attribute { public ValueRange ValRange { get; set; } }
然后我试图将此属性分配给相邻类中的属性:
public class Foo { [MyAttr(ValRange= new ValueRange())] public string Prop { get; set; } }
但是,编译器抱怨如下:
‘ValRange’不是有效的命名属性参数,因为它不是有效的属性参数类型
我也尝试将ValueRange类转换为struct
,希望成为值类型可以解决问题。 有没有办法解决?
有没有办法解决?
没有。
有关更多详细信息,请参阅C#4规范的第17.1.3节,为方便起见,我在此重现:
属性类的位置和命名参数类型仅限于属性参数类型,它们是:
- 以下类型之一:bool,byte,char,double,float,int,long,sbyte,short,string,uint,ulong,ushort。
- 类型对象。
- System.Type类型。
- 枚举类型,前提是它具有公共可访问性,并且嵌套类型(如果有)也具有公共可访问性。
- 上述类型的一维arrays。
不具有这些类型之一的构造函数参数或公共字段不能用作属性规范中的位置参数或命名参数。
请记住,属性的要点是在编译时将信息添加到与您放置属性的实体关联的元数据。 这意味着与该属性相关联的所有信息必须具有明确定义的,明确的方式来将其序列化为元数据。 通过将合法类型集限制为所有可能类型的一小部分,我们确保编译器始终可以发出消费者可以理解的合法元数据。
属性参数值需要在编译时可解析(即常量)。
请参阅MSDN上的Attribute Parameter Types
:
传递给属性的值必须在编译时为编译器所知。
如果您可以创建一个常量的ValueRange
,则可以使用它。
属性参数必须是以下类型的值(引用文章):
- 简单类型(bool,byte,char,short,int,long,float和double)
- 串
- 系统类型
- 枚举
- object(对象类型的属性参数的参数必须是上述类型之一的常量值。)
- 任何上述类型的一维arrays
编辑:将“编译时常量”更改为“值”,因为类型和数组不是常量(感谢指出这一点的评论者(并且由于某种原因随后删除了他的评论……))
属性只能接收编译时常量作为参数(例如3,“hello”,typeof(MyClass),“定义所需非常数据的资源的路径”)。
我给出的最后一个示例(传递类型)可以帮助您设计一个变通方法(传递一个使用您需要的方法实现接口的类型)。
有没有办法解决?
是。
您可以让您的属性使用Type
属性,然后使用实现已定义接口的类型,处理该属性的代码必须承担这些类型 ,因此也会为其客户创建一个隐含但希望记录的要求:
public interface IValueRange { int Start { get; } int End { get; } } public class MyAttr : Attribute { // The used type must implement IValueRange public Type ValueRangeType { get; set; } } // .... public class Foo { class FooValueRange : IValueRange { public int Start { get { return 10; } } public int End { get { return 20; } } } [MyAttr(ValueRangeType = typeof(FooValueRange))] public string Prop { get; set; } }
这与System.ComponentModel
命名空间中的许多类(如DesignerAttribute
。