当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《使用paramsobject[]构造函数的属性会产生不一致的编译器错误》经验,为你挑选了2个好方法。

我收到了错误

属性参数必须是属性参数类型的常量表达式,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似乎解决了这个问题,但这只是一种解决方法.这样做的正确方法是什么?



1> Eric Lippert..:

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[] { ... }并自己将参数放入数组中.



2> AAAbbbCCCddd..:

假设你的构造函数是

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)?

推荐阅读
赛亚兔备_393
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有