【问题标题】:Attribute argument must be a constant error when using an optional parameter in the attribute constructor在属性构造函数中使用可选参数时,属性参数必须是常量错误
【发布时间】:2012-01-07 14:49:18
【问题描述】:

谁能解释为什么这段代码有效:

public class AdministratorSettingValidationAttribute : Attribute
{
    public AdministratorSettingValidationAttribute(AdministratorSettingDataType administratorSettingDataType)
    {
        DataType = administratorSettingDataType;
    }

    public AdministratorSettingValidationAttribute(AdministratorSettingDataType administratorSettingDataType, Type enumerationType)
    {
        DataType = administratorSettingDataType;
        EnumerationType = enumerationType;
    }
}

...但将其重构为使用可选参数:

    public AdministratorSettingValidationAttribute(AdministratorSettingDataType administratorSettingDataType, Type enumerationType = null)
    {
        DataType = administratorSettingDataType;
        EnumerationType = enumerationType;
    }

...导致编译时错误: “属性参数必须是属性参数类型的常量表达式、typeof表达式或数组创建表达式”。

【问题讨论】:

    标签: c# .net visual-studio-2010


    【解决方案1】:

    更新

    该错误已于去年 7 月报告,现已修复。该修复程序将出现在下一版本的 C# 中。有关详细信息,请参阅此 Connect 反馈项:

    http://connect.microsoft.com/VisualStudio/feedback/details/574497/optional-parameter-of-type-string-in-a-attribute-constructor-cannot-be-null


    这显然是一个编译器错误。感谢您提请我注意。

    这里应该发生的是编译器应该意识到可选值表达式被隐式转换为形参类型,然后将该表达式视为该类型的常量表达式。它实际上所做的是将表达式视为无类型的 null 文字,这是错误的。

    您可以通过将常量转换为显式类型来解决该错误:

    public AdministratorSettingValidationAttribute(AdministratorSettingDataType administratorSettingDataType, Type enumerationType = (Type)null) 
    

    修复可能很简单,但我不能保证修复会出现在下一个 C# 版本中;我不确定目前进行非关键错误修复的时间表是什么样的。

    再次感谢,给您带来的不便深表歉意。

    【讨论】:

    • 正如我对上述答案的评论中所述,Mono 可以很好地处理代码,因此它特定于 Visual C# 编译器。
    • 在 Visual Studio 2010 中,如果我有“string myString = (string)null”,它仍然不起作用(即使使用显式转换)。该链接还给了我一个找不到的页面。
    • @user276648 该修复似乎仅在同一程序集中使用该属性时才有效(其中定义了属性类)。如果该属性是在首先编译的一个程序集中定义的,并且该属性应用于另一个程序集中的代码元素(并且另一个程序集引用了第一次编译的二进制文件),则修复不起作用。如果您使用空字符串"" 作为可选字符串参数的默认值,则可以绕过它。
    • string myString = (string) null 对我不起作用。 private const string NullString = null 也没有使用它。我添加了一个调用this(val1, null)的重载构造函数。
    • 这个错误在 MSVS2013 中仍然存在,但是对于可以为空的类型 pastebin.com/Y9iCHcT0 字符串是可以的。
    【解决方案2】:

    对我来说,这闻起来像是一个编译器错误。属性类是“特殊”类,它们可以用作元数据。 C# 编译器允许您以不同于普通类的方式使用它们,因此我们可以假设 C# 编译器中存在用于编译属性类的(部分)自定义实现。 (任何人都可以在单声道上测试这个吗?)

    也就是说,我做了一些测试,发现只有在使用属性的构造函数将参数的默认值指定为 null 而没有定义该属性的自定义值时,编译器才会给我们一个错误。我的测试代码:

    class TestAttribute : Attribute
    {
        public TestAttribute(object test = null) { }
        //public TestAttribute(int test = 0) { } 
    
        public void TestMethod(object test = null) { }
    }
    
    class TestClass
    {
        public TestClass(object test  = null) { }
    }
    
    [Test()] // crashes
    //[Test()] // Works when using the constructor taking an int
    //[Test(null)] // works
    class Program
    {
        static void Main(string[] args)
        {
            TestClass t = new TestClass(); // works
    
            TestAttribute a = typeof(Program).GetCustomAttributes(typeof(TestAttribute), false).Cast<TestAttribute>().First();
    
            a.TestMethod(); // works
        }
    }
    

    (在 .NET 4.0 下使用 VS 2010 进行测试,任何人都可以使用 mono 进行测试吗?)

    请注意,属性已经允许您将属性视为可选的,因此您可以将可选参数设为属性(如果还没有,并将其从构造函数中删除。这仍然允许您编写 [Test(null, MyProperty = null)]

    【讨论】:

    • 哇...这可能意味着如果在调用时使用可选参数,编译器会生成一些代码以某种方式计算省略参数的值,这与常量值要求冲突吗?这可以解释这个问题,虽然我认为应该有一个更容易理解的错误信息。
    • @PavelGatilov - 不,它不能。第一个观察结果是,在构造函数中使用除 null 以外的默认值完全没问题。第二个观察是属性的构造函数将像普通代码一样执行,每次访问它时(Type.GetCustomAttributes(...))。我认为我们需要为此唤醒 Eric Lippert
    • @PavelGatilov - 使用属性时。编译器会将属性用法编译为您指定属性的位置的元数据。在这里,有一个名为 ctor args 的字段。使用默认参数时,使用属性时不指定参数,该字段会包含'nothing',意思是JIT会为我们确定默认值。
    • 在 Windows 上使用 Mono 2.10 测试,编译和运行顺利。
    猜你喜欢
    • 2013-02-09
    • 2016-01-05
    • 2014-11-13
    • 1970-01-01
    • 2017-02-24
    • 1970-01-01
    • 1970-01-01
    • 2014-11-09
    相关资源
    最近更新 更多