我收到了错误
属性参数必须是属性参数类型的常量表达式,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似乎解决了这个问题,但这只是一种解决方法.这样做的正确方法是什么?
tldr:
正确的解决方法是告诉编译器不要使用扩展形式:
[DataRow(new[] { 1 }, new object[] { new[] { "1" } })]
过度分析:
迈克尔兰德尔的答案基本上是正确的.让我们通过简化您的示例来深入研究:
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() { } }
我们首先考虑非错误情况.
[MyAttribute()]
正常形式没有足够的参数.构造函数适用于其扩展形式.编译器编译它就像你写的那样:
[MyAttribute(new object[0])]
接下来,怎么样
[MyAttribute(new int[0])]
?现在我们必须确定构造函数是否适用于其正常形式或扩展形式.它不适用于正常形式,因为int[]
不可转换为object[]
.它适用于扩展形式,因此编译就像您编写的一样
[MyAttribute(new object[1] { new int[0] } )]
那怎么样?
[MyAttribute(new object[0])]
构造函数适用于其正常形式和扩展形式.在那种情况下,正常形式获胜.编译器生成写入的调用.它不会将对象数组包装在第二个对象数组中.
关于什么
[MyAttribute(new string[0], new string[0])]
?普通形式的论据太多了.使用扩展形式:
[MyAttribute(new object[2] { new string[0], new string[0] })]
这一切都应该是直截了当的.然后出了什么问题:
[MyAttribute(new string[0])] // ERROR
?那么,首先,它是否适用于正常或扩展形式?显然它适用于扩展形式.不太明显的是,它也适用于正常形式. int[]
不会隐式转换为object[]
但string[]
确实如此!这是一个不安全的协变数组引用转换,它在我的"最差C#特性"列表中名列前茅.
由于重载决策表明这适用于普通形式和扩展形式,因此正常形式获胜,并且编译就像您编写的一样
[MyAttribute((object[]) new string[0] )] // ERROR
让我们探讨一下.如果我们修改上面的一些工作案例:
[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] })]
所有这些现在都在早期版本的C#中失败,并在当前版本中取得成功.
显然,编译器之前允许没有转换,甚至没有一个标识转换,对象阵列上.现在它允许身份转换,但不允许协变数组转换.
允许通过编译时常量值分析处理的转换; 你可以做
[MyAttribute(new int[1] { (int) 100} )]
如果你愿意,因为常量分析器会删除转换.但属性分析器不知道如何处理意外的转换object[]
,因此它会产生错误.
你提到的另一个案子怎么样? 这是有趣的!
[MyAttribute((object)new string[0])]
再说一次,让我们理解它.这仅适用于其扩展形式,因此应该像编写时一样进行编译
[MyAttribute(new object[1] { (object)new string[0] } )]
但那是合法的.为了保持一致,要么这两种形式都应该是合法的,要么两者都应该是非法的 - 坦率地说,我并不关心这两种方式 - 但是一方是合法的而另一方是不合法的,这是奇怪的.考虑报告错误.(如果这实际上是一个错误,那可能是我的错.很抱歉.)
它的长期和短期是: 将params对象[]与数组参数混合是一种混淆的方法.尽量避免它.如果您正在将数组传递给params object []方法,请以正常形式调用它.制作一个new object[] { ... }
并自己将参数放入数组中.
假设你的构造函数是
public Foo(params object[] vals) { }
然后我认为你正在遇到一些被忽视的非显而易见的编译器Dark Magic.
例如,显然下面的内容可行
[Foo(new object[] { "abc", "def" },new object[] { "abc", "def" })] [Foo(new string[] { "abc", "def" },new string[] { "abc", "def" })]
这对我也有用
[Foo(new [] { 2 }, new [] { "abc"})] [Foo(new [] { 1 }, new [] { "a"})]
但事实并非如此
[Foo(new [] { "a" })] [Foo(new [] { "aaa"})] [Foo(new string[] { "aaa" })]
属性参数必须是属性参数类型的常量表达式,typeof表达式或数组创建表达式
我认为这里的关键信息是关键
具有params数组的方法可以以"正常"或"扩展"形式调用.正常形式就好像没有"参数".扩展形式使用参数并将它们捆绑成一个自动生成的数组.如果两种形式都适用,则普通形式胜过扩展形式.
举个例子
PrintLength(new string[] {"hello"}); // normal form PrintLength("hello"); // expanded form, translated into normal form by compiler.
当给出适用于两种形式的调用时,编译器总是在扩展形式上选择普通形式.
但是我认为这再次变得更加混乱object[]
甚至属性.
我不会假装我确切地知道CLR在做什么(而且还有更多合格的人可以回答).但是为了参考,请查看CLR SO向导 Eric Lippert的类似答案,以更详细地说明可能发生的事情
C#params object []奇怪的行为
为什么params表现得像这样?
有没有办法从myFunc(new int [] {1,2,3}}中删除myFunc(1,2,3)?