所以我有一个自定义属性,我们称之为MyCustomAttribute
,它有一个像这样的构造函数:
public MyCustomAttribute(int? i)
{
// Code goes here
}
并声明一个属性:
public int? DefaultProperty { get; set; }
现在,如果我想使用该属性,我需要传入int
或null
,这就是我所期望的。
但这会产生编译错误:
[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
。
这是为什么?如果我更改构造函数以获取int,我可以传入0
,但不传递null
,这会破坏值的目的(有时可能为null)
答案 0 :(得分:22)
原因是虽然0
和null
都是常量,但初始化构造函数参数所需的实际表达式却不是。两者都要求转换为有效的int?
表达式。在引擎盖下,它基本上生成以下
[MyCustomAttribute(new Nullable<int>(0))]
此表达式不是常量,作为属性参数
是不合法的编辑
dtb询问这在属性值中是否非法为什么为可为空的参数设置默认参数是合法的?
void Example(int? x = null);
您需要考虑的是解释价值的人/是什么。
对于属性参数,该值由CLR解释。自1.0以来,关于法律属性值的规则并没有真正改变。 Nullable不存在,因此CLR不理解可以为空的初始化模式。
对于默认参数,vaule由方法的调用站点处的编译器解释。编译器理解可以为空的值,并且有更多的工作空间,因为它可以根据IL中的值在调用站点创建非常量表达式。
它是如何实际工作的?首先,编译器实际上将在IL
中以不同方式编码常量和null
// x = 0
[DefaultParameterValue(0)]
// x = null
[DefaultParameterValue(null)]
在调用站点,编译器检查这些值并为参数值创建适当的(非常量)表达式。
答案 1 :(得分:11)
我知道问题是为什么,但对于重定向到此页面并正在寻求答案的开发人员。这是我的解决方法。
public class MyCustomAttribute: Attribute
{
private Nullable<bool> myBoolean = null;
public bool MyBooleanProperty { get { return this.myBoolean.GetValueOrDefault(); } set { this.myBoolean = value; } }
public bool IsMyBooleanPropertySpecified { get { return this.myBoolean != null; } }
}
答案 2 :(得分:4)
使用属性的经验法则是,可以使用const
关键字声明它们所采用的参数类型 - 即所有基本类型(int
,char
等)和{ {1}}。在属性参数列表中不能使用string
关键字
你不能使用这样的属性:
new
甚至是这样(即使[Custom(new object())]
class Class {
}
是值类型):
DateTime
因此也不允许使用[Custom(new DateTime(2001,1,1))]
class Class {
}
,因为传递null或Nullable<T>
等同于这样做:
1
答案 3 :(得分:2)
我知道这是一个老问题,但没有人真正回答为什么 Nullable&lt;&gt;不被允许。这个结论性的答案在于the documentation for compiler error CS0655:
属性类的位置和命名参数仅限于 以下属性参数类型:
- 以下类型之一:bool,byte,char,double,float,int,long,sbyte,short,string,uint,ulong或ushort。
- 类型对象。
- 类型System.Type。
- 枚举类型,前提是它具有公共可访问性,并且嵌套它的任何类型也具有公共可访问性。
- 前述类型的一维数组。
此文档页面适用于Visual Studio 2008,但我还没有听说过这方面的任何最新变化。
答案 4 :(得分:2)
我认为在访问变量之前忘记检查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
答案 5 :(得分:0)
一个人买了一辆法拉利,但有些傻瓜将州长(限制车速的装置)设定为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。当然,对可以为空的int进行编译时检查会很好,但这已经足够了。所以直接撕掉那个州长。