属性构造函数或属性中的Nullable int

所以我有一个自定义属性,让我们称它为MyCustomAttribute ,它有一个像这样的构造函数:

 public MyCustomAttribute(int? i) { // Code goes here } 

并声明一个属性:

 public int? DefaultProperty { get; set; } 

现在,如果我想使用该属性,我需要传入一个intnull ,这就是我所期望的。

但这会给编译器错误:

 [MyCustomAttribute(1, DefaultProperty = 1)] public int? MyProperty { get; set; } 

这样做:

 [MyCustomAttribute(null,DefaultProperty = null)] public int? MyProperty { get; set; } 

错误是: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type构造函数和属性An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type

为什么是这样? 如果我更改构造函数以获取int,我可以传入0 ,但不能为null ,这会破坏值的目的(有时可能为null)

原因是虽然0null都是常量,但初始化构造函数参数所需的实际表达式却不是。 两者都要求转换为有效的int? 表达。 在引擎盖下它基本上产生以下

 [MyCustomAttribute(new Nullable(0))] 

此表达式不是常量,作为属性参数不合法

编辑

dtb询问这在属性值中是否非法为什么为可为空的参数设置默认参数是合法的?

 void Example(int? x = null); 

您需要考虑的是解释价值的人/是什么。

对于属性参数,该值由CLR解释。 自1.0以来,关于法律属性值的规则并没有真正改变。 Nullable不存在,因此CLR不理解可空的初始化模式。

对于默认参数,vaule由方法的调用站点处的编译器解释。 编译器理解可以为空的值,并且有更多的工作空间,因为它可以根据IL中的值在调用站点创建非常量表达式。

它是如何实际工作的? 首先,编译器实际上将编码常量并在IL中以不同方式进行null

 // x = 0 [DefaultParameterValue(0)] // x = null [DefaultParameterValue(null)] 

在调用站点,编译器检查这些值并为参数值创建适当的(非常量)表达式。

我知道问题是为什么,但对于重定向到此页面并正在寻求答案的开发人员。 这是我的解决方法。

 public class MyCustomAttribute: Attribute { private Nullable myBoolean = null; public bool MyBooleanProperty { get { return this.myBoolean.GetValueOrDefault(); } set { this.myBoolean = value; } } public bool IsMyBooleanPropertySpecified { get { return this.myBoolean != null; } } } 

使用属性的经验法则是它们采用的参数类型可以用const关键字声明 – 即所有基本类型( intchar等)和string 。 在属性参数列表中不能使用new关键字

你不能使用这样的属性:

 [Custom(new object())] class Class { } 

或者甚至喜欢这样(即使DateTime是值类型):

 [Custom(new DateTime(2001,1,1))] class Class { } 

因此,也不允许使用Nullable ,因为传递null或1相当于这样做:

 [Custom(new Nullable())] //[Custom(null)] class Class { } [Custom(new Nullable(1))] //[Custom(1)] class Class { } 

我想在访问变量之前忘记检查IsPropertyXSet会很容易。 我认为使用可空的更好。 所以这是一种可能的方法来构建,同时保持可空:

 public class FooAttribute : Attribute { public bool? SomeFlag { get; set; } public bool SetSomeFlag { get { throw new Exception("should not be called"); // required for property visibility } set { SomeFlag = value; } } } [Foo(SetSomeFlag=true)] public class Person { } [Foo] public class Person2 { // SetSomeFlag is not set } bool? b1 = ((FooAttribute)typeof(Person).GetCustomAttributes(typeof(FooAttribute), false)[0]).SomeFlag; // b1 = true bool? b2 = ((FooAttribute)typeof(Person2).GetCustomAttributes(typeof(FooAttribute), false)[0]).SomeFlag; // b2 = null 

我知道这是一个老问题,但没有人真正回答为什么不允许Nullable <>。 这个结论性的答案在于编译器错误CS0655的文档 :

属性类的位置和命名参数仅限于以下属性参数类型:

  • 以下类型之一:bool,byte,char,double,float,int,long,sbyte,short,string,uint,ulong或ushort。
  • 类型对象。
  • System.Type类型。
  • 枚举类型,前提是它具有公共可访问性,并且嵌套它的任何类型也具有公共可访问性。
  • 前述类型的一维数组。

此文档页面适用于Visual Studio 2008,但我还没有听说过此领域的任何最新更改。

一个人买了一辆法拉利,但有些傻瓜将州长(限制车速的装置)设定为30英里/小时。 这家伙不能把州长改成更合理的东西,所以他把它撕掉了。 现在法拉利可以像发动机一样快速发力。

Microsoft不允许您将nullables用作自定义属性属性。 但是,Microsoft确实允许您将字符串(可以为null)用作自定义属性属性。 所以完全消除了这个限制。 用字符串替换你的nullable int。 当然,字符串比可以为空的int更具限制性,因为它可以具有非整数值,例如“bob”,但这是你为微软搞砸自定义属性(一种语言特性)为CLR中的实现细节付出的代价。应该是无关紧要的。

这是我的例子。

 public abstract class Contract : Attribute, IContract { public abstract void Check (Object root, String path, Object valueAtPath); } public sealed class DecimalPlacesContract : Contract { public String MinimumMantissaCount { get { return minimumMantissaCount?.ToString(); } set { minimumMantissaCount = value == null ? (int?) null : Int32.Parse(value); } } public String MaximumMantissaCount { get { return maximumMantissaCount?.ToString(); } set { maximumMantissaCount = value == null ? (int?) null : Int32.Parse(value); } } public String MinimumSignificantDigitCount { get { return minimumSignificantDigitCount?.ToString(); } set { minimumSignificantDigitCount = value == null ? (int?) null : Int32.Parse(value); } } public String MaximumSignificantDigitCount { get { return maximumSignificantDigitCount?.ToString(); } set { maximumSignificantDigitCount = value == null ? (int?) null : Int32.Parse(value); } } private int? minimumMantissaCount; private int? maximumMantissaCount; private int? minimumSignificantDigitCount; private int? maximumSignificantDigitCount; public override void Check (Object root, String path, Object valueAtPath) { decimal? value = valueAtPath as decimal?; int mantissaCount = DecimalMisc.GetMantissaDigitCount(value ?? 0); int significantDigitCount = DecimalMisc.GetSignificantDigitCount(value ?? 0); if (value == null || mantissaCount < minimumMantissaCount || mantissaCount > maximumMantissaCount || significantDigitCount < minimumSignificantDigitCount || significantDigitCount > maximumSignificantDigitCount) { throw new ContractException(this, root, path, valueAtPath); } } } 

以下是如何使用自定义属性属性。

  private class Dollar { [DecimalPlacesContract (MinimumMantissaCount = "0", MaximumMantissaCount = "2")] public decimal Amount { get; set; } } private class DollarProper { [DecimalPlacesContract (MinimumSignificantDigitCount = "2", MaximumSignificantDigitCount = "2")] public decimal Amount { get; set; } } 

只需在值周围添加引号即可。 未指定的属性默认为null。

当您从Type或PropertyInfo实例调用GetCustomAttributes或GetCustomAttribute时,任何不正确的值(如“bob”)都将导致抛出FormatException。 当然,对nullable int进行编译时检查会很好,但这已经足够了。 所以直接撕掉那个州长。