string.Format在运行时因整数数组而失败
考虑string.Format()
其参数是一个字符串,以及在重载列表中的一个object[]
或许多对象。
该声明成功:
string foo = string.Format("{0} {1}", 5, 6);
就像这样:
object[] myObjs = new object[] {8,9}; string baz = string.Format("{0} and {1}", myObjs;
和字符串数组一样:
string[] myStrings = new string[] {"abc", "xyz"}; string baz = string.Format("{0} {1}", myStrings);
似乎整数,当单独指定时,可以加框或强制类型object
,而object
又被强制转换为字符串。
此语句在运行时失败。
int[] myInts = new int[] {8,9}; string bar = string.Format("{0} and {1}", myInts);
索引(从零开始)必须大于或等于零且小于参数列表的大小。
- 为什么不能或不能将int数组强制或装箱到
object[]
或string[]
? - 出于一点点好奇心,为什么编译器没有抓住这个?
调用失败的原因相同,以下内容也将失败:
string foo = string.Format("{0} {1}", 5);
您正在以format
指定两个参数,但仅指定一个对象。
编译器没有捕获它,因为int[]
作为一个对象传递,该对象是函数的完全有效的参数。
另请注意,数组协方差不适用于值类型,因此您无法执行以下操作:
object[] myInts = new int[] {8,9};
但是你可以逃脱:
object[] myInts = new string[] { "8", "9" }; string bar = string.Format("{0} {1}", myInts);
这将是有效的,因为你将使用接受object[]
的String.Format
重载。
您的电话会被翻译成:
string foo = string.Format("{0} {1}", myInts.ToString());
这导致了这个字符串:
string foo = "System.Int32[] {1}";
因此{1}没有参数,所以会引发exception
我认为你遇到问题的概念是为什么int[]
不会转换为object[]
。 这是一个示例,说明为什么会这么糟糕
int[] myInts = new int[]{8,9}; object[] myObjs = (object[])myInts; myObjs[0] = new object();
问题是我们只是将一个对象添加到一个int数组中。
所以在你的代码中发生的事情是myInts
被myInts
为object
而你没有第二个参数来填充{1}
你的string.Format期待2个参数({0}和{1})。 您只提供1个参数(int [])。 你需要更像这样的东西:
string bar = string.Format("{0} and {1}", myInts[0], myInts[1]);
编译器没有注意到该问题,因为格式字符串是在运行时计算的。 IE编译器不知道{0}和{1}意味着应该有2个参数。
这有效:
string bar = string.Format("{0} and {1}", myInts[0], myInts[1]);
编译器不会捕获它,因为它不会评估您的格式字符串。
你放弃顶部的例子与你在下面尝试做的不匹配…你提供了两个{}和两个参数,但在底部你只提供了一个参数。
这是一个很老的问题,但我最近遇到了同样的问题。 我还没有看到一个对我有用的答案,所以我会分享我找到的解决方案。
- 为什么不能或不能将int数组强制或装箱到对象[]或字符串[]?
为什么它没有盒装,我不知道。 但它可以明确加框,请参阅下面的解决方案。 - 为什么编译器没有抓到这个?
因为编译器误解了这种情况:类型不完全是一个对象数组,因此它不知道如何处理它并决定在int数组上执行.ToString()
,它返回一个包含该类型的单个参数名称而不是参数列表本身。 它不会对字符串数组执行此操作,因为目标类型已经是字符串 – 但是对于任何其他类型的数组,都会发生相同的问题(例如bool[]
)。
考虑var arr1 = new int[]{1,2};
withstring.Format("{0}", arr1)
:只要格式字符串中只有{0}
,就会得到类型名称"System.Int32[]"
(并且不会发生exception)。
如果你有更多的占位符,例如string.Format("{0}{1}", arr1)
,则会发生exception – 因为arr1
被误解为一个参数 – 对于编译器, 第二个缺失。
但我认为是一个概念性的错误是你无法转换arr1
,即如果你试图做(object[])arr1
– 你得到:
CS0030无法将类型’int []’转换为’object []’
解:
填充int数组的每个元素不是一个适合我的解决方案,因为在我的项目中,我在包含{0}...{n}
运行时期间动态创建格式模板字符串 – 因此我需要传递一个数组到String.Format
。
所以我找到了以下解决方法。 我创建了一个通用的辅助函数(如果你愿意,它当然也可以是一个扩展方法):
// converts any array to object[] and avoids FormatException object[] Convert(T[] arr) { var obj = new List
现在,如果您在下面显示FormatException的示例中尝试:
// FormatException: Index (zero based) must be greater than or equal to zero // and less than the size of the argument list var arr1 = (new int[] { 1, 2 }); string.Format("{0}{1}{0}{1}", arr1).Dump();
修复:使用
Convert(arr1)
作为string.Format(...)
第二个参数 ,如下所示:
// Workaround: This shows 1212, as expected var arr1 = (new int[] { 1, 2 }); string.Format("{0}{1}{0}{1}", Convert(arr1)).Dump();
试试DotNetFiddle的例子
结论:看起来,.NET运行时通过对它应用.ToString()
实际上错误解释了该参数,如果它还不是object[]
类型的话。 Convert
方法为运行时提供了其他选择,而不是以正确的方式执行,因为它返回了预期的类型。 我发现显式类型转换不起作用,因此需要辅助函数。
注意:如果您在循环中多次调用该方法并且您关注速度,您还可以将所有内容转换为可能最有效的字符串数组:
// converts any array to string[] and avoids FormatException string[] ConvertStr(T[] arr) { var strArr = new string[arr.Length]; for (int i = 0; i < arr.Length; i++) { strArr[i]=arr[i].ToString(); } return strArr; }
这也很有用。 要从不同的数据类型(如字典)进行转换,您只需使用即可
string[] Convert(Dictionary coll) { return ConvertStr(coll.Values.ToArray()); }