使用params object []构造函数的属性会产生不一致的编译器错误

Dan*_*man 8 c# .net-attributes

我收到了错误

属性参数必须是属性参数类型的常量表达式,typeof表达式或数组创建表达式

请注意下面的截图:

在此输入图像描述

请注意,如果我将DataRow属性与一个或三个参数一起使用,则不会出现编译错误.但是如果我使用两个参数而第二个参数是一个字符串数组,那么我确实会遇到编译错误.

DataRowAttribute的签名是public DataRowAttribute (object data1);public DataRowAttribute (object data1, params object[] moreData);

第一个给了我没问题,但第二个似乎变得困惑.

我认为params object[]可能会引起一些混乱.也许它无法确定我的意思[DataRow(new[] { 1 }, new[] { "1" })]还是[DataRow(new[] { 1 }, "1")]

为了解决这个问题,我尝试将第二个属性转换为object([DataRow(new[] { 1 }, (object)new[] { "1" })]),但错误没有消失,它警告我演员阵容是多余的.我也尝试明确指定数组的类型,但这也没有帮助.

我可以添加第三个虚拟参数,即使null似乎解决了这个问题,但这只是一种解决方法.这样做的正确方法是什么?

Eri*_*ert 12

tldr:

正确的解决方法是告诉编译器不要使用扩展形式:

[DataRow(new[] { 1 }, new object[] { new[] { "1" } })]
Run Code Online (Sandbox Code Playgroud)

过度分析:

迈克尔兰德尔的答案基本上是正确的.让我们通过简化您的示例来深入研究:

using System;
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class MyAttribute : Attribute {
    public MyAttribute(params object[] x){}
}
public class Program
{
    [MyAttribute()]
    [MyAttribute(new int[0])]
    [MyAttribute(new string[0])] // ERROR
    [MyAttribute(new object[0])]
    [MyAttribute(new string[0], new string[0])]  
    public static void Main() { }
}
Run Code Online (Sandbox Code Playgroud)

我们首先考虑非错误情况.

    [MyAttribute()]
Run Code Online (Sandbox Code Playgroud)

正常形式没有足够的参数.构造函数适用于其扩展形式.编译器编译它就像你写的那样:

    [MyAttribute(new object[0])]
Run Code Online (Sandbox Code Playgroud)

接下来,怎么样

    [MyAttribute(new int[0])]
Run Code Online (Sandbox Code Playgroud)

?现在我们必须确定构造函数是否适用于其正常形式或扩展形式.它不适用于正常形式,因为int[]不可转换为object[].它适用于扩展形式,因此编译就像您编写的一样

    [MyAttribute(new object[1] { new int[0] } )]
Run Code Online (Sandbox Code Playgroud)

那怎么样?

    [MyAttribute(new object[0])]
Run Code Online (Sandbox Code Playgroud)

构造函数适用于其正常形式和扩展形式.在那种情况下,正常形式获胜.编译器生成写入的调用.它不会将对象数组包装在第二个对象数组中.

关于什么

    [MyAttribute(new string[0], new string[0])]  
Run Code Online (Sandbox Code Playgroud)

?普通形式的论据太多了.使用扩展形式:

    [MyAttribute(new object[2] { new string[0], new string[0] })] 
Run Code Online (Sandbox Code Playgroud)

这一切都应该是直截了当的.然后出了什么问题:

    [MyAttribute(new string[0])] // ERROR
Run Code Online (Sandbox Code Playgroud)

?那么,首先,它是否适用于正常或扩展形式?显然它适用于扩展形式.不太明显的是,它也适用于正常形式. int[]不会隐式转换为object[]string[]确实如此!这是一个不安全的协变数组引用转换,它在我的"最差C#特性"列表中名列前茅.

由于重载决策表明这适用于普通形式和扩展形式,因此正常形式获胜,并且编译就像您编写的一样

[MyAttribute((object[]) new string[0] )] // ERROR
Run Code Online (Sandbox Code Playgroud)

让我们探讨一下.如果我们修改上面的一些工作案例:

    [MyAttribute((object[])new object[0])] // SOMETIMES ERROR!
    [MyAttribute((object[])new object[1] { new int[0] } )]
    [MyAttribute((object[])new object[2] { new string[0], new string[0] })]
Run Code Online (Sandbox Code Playgroud)

所有这些现在都在早期版本的C#中失败,并在当前版本中取得成功.

显然,编译器之前允许没有转换,甚至没有一个标识转换,对象阵列上.现在它允许身份转换,但不允许协变数组转换.

允许通过编译时常量值分析处理的转换; 你可以做

[MyAttribute(new int[1] { (int) 100} )]
Run Code Online (Sandbox Code Playgroud)

如果你愿意,因为常量分析器会删除转换.但属性分析器不知道如何处理意外的转换object[],因此它会产生错误.

你提到的另一个案子怎么样? 这是有趣的!

[MyAttribute((object)new string[0])]
Run Code Online (Sandbox Code Playgroud)

再说一次,让我们理解它.这仅适用于其扩展形式,因此应该像编写时一样进行编译

[MyAttribute(new object[1] { (object)new string[0] } )]
Run Code Online (Sandbox Code Playgroud)

但那是合法的.为了保持一致,要么这两种形式都应该是合法的,要么两者都应该是非法的 - 坦率地说,我并不关心这两种方式 - 但是一方是合法的而另一方是不合法的,这是奇怪的.考虑报告错误.(如果这实际上是一个错误,那可能是我的错.很抱歉.)

它的长期和短期是: 将params对象[]与数组参数混合是一种混淆的方法.尽量避免它.如果您正在将数组传递给params object []方法,请以正常形式调用它.制作一个new object[] { ... }并自己将参数放入数组中.


AAA*_*ddd 5

假设你的构造函数是

public Foo(params object[] vals) { }
Run Code Online (Sandbox Code Playgroud)

然后我认为你正在遇到一些被忽视的非显而易见的编译器Dark Magic.

例如,显然下面的内容可行

[Foo(new object[] { "abc", "def" },new object[] { "abc", "def" })]
[Foo(new string[] { "abc", "def" },new string[] { "abc", "def" })]
Run Code Online (Sandbox Code Playgroud)

这对我也有用

[Foo(new [] { 2 }, new [] { "abc"})]
[Foo(new [] { 1 }, new [] { "a"})]
Run Code Online (Sandbox Code Playgroud)

但事实并非如此

[Foo(new [] { "a" })]
[Foo(new [] { "aaa"})]
[Foo(new string[] { "aaa" })]
Run Code Online (Sandbox Code Playgroud)

属性参数必须是属性参数类型的常量表达式,typeof表达式或数组创建表达式

我认为这里的关键信息是关键

具有params数组的方法可以以"正常"或"扩展"形式调用.正常形式就好像没有"参数".扩展形式使用参数并将它们捆绑成一个自动生成的数组.如果两种形式都适用,则普通形式胜过扩展形式.

举个例子

PrintLength(new string[] {"hello"}); // normal form
PrintLength("hello"); // expanded form, translated into normal form by compiler.
Run Code Online (Sandbox Code Playgroud)

当给出适用于两种形式的调用时,编译器总是在扩展形式上选择普通形式.

但是我认为这再次变得更加混乱object[]甚至属性.

我不会假装我确切地知道CLR在做什么(而且还有更多合格的人可以回答).但是为了参考,请查看CLR SO向导 Eric Lippert的类似答案,以更详细地说明可能发生的事情

C#params object []奇怪的行为

为什么params表现得像这样?

有没有办法从myFunc(new int [] {1,2,3}}中删除myFunc(1,2,3)?