好的,假设我们放弃其他问题,您错误地认为其中任何一个是编译器错误,并实际回答您真正的问题。
首先,让我们尝试陈述真正的问题。这是我的看法:
导言:
“可变参数”方法是一种可以接受预先未指定数量参数的方法。
C#中实现可变参数方法的标准方式是:
void M(T1 t1, T2 t2, params P[] p)
也就是说,零个或多个必需的参数后跟一个标记为 "params" 的数组。
调用这样的方法时,该方法可以使用其普通形式(不带 params)或其扩展形式(带 params)。也就是说,对于以下调用:
void M(params object[] x){}
表格格式
M(1, 2, 3)
生成为
M(new object[] { 1, 2, 3 });
因为它只适用于其扩展形式。但是一个调用
M(new object[] { 4, 5, 6 });
生成为
M(new object[] { 4, 5, 6 });
而不是
M(new object[] { new object[] { 4, 5, 6 } });
因为它适用于其正常形式。
C#支持对引用类型元素的数组进行不安全的数组协变。也就是说,一个 string[]
可以隐式转换为 object[]
,即使试图将这样一个数组的第一个元素更改为非字符串也会产生运行时错误。
问题:
我想要进行以下形式的调用:
M(new string[] { "hello" });
并且让它的行为就像该方法只适用于扩展形式:
M(new object[] { new string[] { "hello" }});
而不是正常形式:
M((object[])(new string[] { "hello" }));
有没有一种在C#中实现可变参数方法的方式,不会受到不安全数组协变和方法优先适用于其正常形式的影响?
答案是肯定的,但你可能不喜欢它。如果你打算将单个数组传递给它,最好将方法设置为非可变参数方法。
微软的C#实现支持一种未记录的扩展,允许使用C风格的可变参数方法而不使用params数组。这种机制并不是为了普遍使用而设计的,只包括CLR团队和其他编写互操作库的人员,以便他们编写桥接C#和期望C风格可变参数方法的语言之间的互操作代码。我强烈建议不要尝试自己做。
这样做的机制涉及使用未记录的__arglist关键字。一个基本的草图是:
public static void M(__arglist)
{
var argumentIterator = new ArgIterator(__arglist);
object argument = TypedReference.ToObject(argumentIterator.GetNextArg());
你可以使用参数迭代器的方法来遍历参数结构并获取所有参数。你还可以使用超级神奇的类型引用对象来获取参数的类型。使用这种技术甚至可以将变量的引用作为参数传递,但我不建议这样做。
这种技术特别糟糕的地方在于调用者需要这样说:
M(__arglist(new string[] { "hello" }));
在 C# 中,使用可变参数方法看起来相当糟糕。现在你知道为什么最好完全放弃可变参数方法;只需让调用方传递一个数组就可以了。
再次提醒,(1)切勿尝试使用这些未经记录的 C# 语言扩展,它们是 CLR 实现团队和交互操作库作者的便利工具,(2)应该放弃可变参数方法;因为它们似乎不适合您的问题空间。不要与工具作斗争;选择其他工具。
obs[0]
是一个数组呢?obs
是int[]
,它的类型在编译时已知,其元素只能是int
类型。如果你有params object[]
,那么你可能会认为object[]
也是一个object
,但这是没有意义的。 - vgruparams object[]
,那就没问题了。看看我简短但完整的程序示例... - Jon Skeet