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数组中。

所以在你的代码中发生的事情是myIntsmyIntsobject而你没有第二个参数来填充{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}; with string.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(); foreach (var item in arr) { obj.Add((object)item); } return obj.ToArray(); } 

现在,如果您在下面显示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()); }