string.Format在运行时因整数数组而失败

时间:2010-07-15 17:24:19

标签: c# arrays string-formatting

考虑其参数为字符串的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,而int[] myInts = new int[] {8,9}; string bar = string.Format("{0} and {1}", myInts); 则强制转换为字符串。

此声明在运行时时失败。

object[]
  

索引(从零开始)必须大于或等于零且小于参数列表的大小。

  • 为什么不强制或不能将int数组强制或装箱到string[]或{{1}}?
  • 出于一点点好奇心,编译器为什么不抓住这个?

7 个答案:

答案 0 :(得分:25)

呼叫失败的原因相同,以下内容也将失败:

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

这会有效,因为您将使用接受String.Format的{​​{1}}重载。

答案 1 :(得分:3)

您的电话会被翻译成:

string foo = string.Format("{0} {1}", myInts.ToString());

导致此字符串:

string foo = "System.Int32[] {1}";

因为{1}没有参数,所以它会引发异常

答案 2 :(得分:2)

我认为您遇到问题的概念是int[]未投放到object[]的原因。这是一个示例,说明为什么会这么糟糕

int[] myInts = new int[]{8,9};
object[] myObjs = (object[])myInts;
myObjs[0] = new object();

问题是我们刚刚将一个对象添加到一个int数组中。

因此,您的代码中发生的事情是myInts被强制转换为object并且您没有第二个参数来填充{1}

答案 3 :(得分:1)

这是一个很老的问题,但我最近遇到了同样的问题。我还没有看到一个对我有用的答案,所以我会分享我找到的解决方案。

  • 为什么不能或不能将int数组强制或装箱到对象[]或字符串[]?
    为什么它没有盒装,我不知道。但可以明确加框,请参阅下面的解决方案。
  • 为什么编译器没有捕到它?由于编译器误解了这种情况:类型不完全是一个对象数组,因此它不知道如何处理它并决定在int数组上执行.ToString(),它返回一个包含类型名称的单个参数,而不是参数列表本身。它不会对字符串数组执行此操作,因为目标类型已经是字符串 - 但是对于任何其他类型的数组,都会发生相同的问题(例如bool[])。
    考虑使用var arr1 = new int[]{1,2}; string.Format("{0}", arr1):只要格式字符串中只有{0},就只会返回类型名称"System.Int32[]"(并且不会发生异常) )。
    如果你有更多的占位符,例如string.Format("{0}{1}", arr1),然后发生异常 - 因为arr1被误解为一个参数 - 而对于编译器, a 2 nd one < / em>缺失。
    但我认为一个概念性的错误是你无法转换arr1,即如果你试图做(object[])arr1 - 你得到:
      

    CS0030无法将类型'int []'转换为'object []'

<强>解决方案:

填充int数组的每个元素不是一个适合我的解决方案,因为在我的项目中,我在包含{0}...{n}的运行时期间动态创建格式模板字符串 - 因此我需要传递一个数组到String.Format

所以我找到了以下解决方法。我创建了一个通用的辅助函数(如果你愿意的话,它当然也可以是一个扩展方法):

// converts any array to object[] and avoids FormatException
object[] Convert<T>(T[] arr)
{
    var obj = new List<object>();
    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) 作为 2 nd 参数作为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>(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<K,V>(Dictionary<K,V> coll)
{
    return ConvertStr<V>(coll.Values.ToArray());
}

这也很有用。要从不同的数据类型(如字典)进行转换,只需使用

即可
var baz = string.Format("{0} and {1}", myInts.Select(s => $"{s}").ToArray());

更新:使用字符串插值,另一种解决方法是:

res.pca <- prcomp(dataset, scale=TRUE)
fviz_eig(res.pca)

答案 4 :(得分:1)

使其工作的捷径(虽然不是最佳的):

int[] myInts = new int[] { 8, 9 };
string[] myStrings = Array.ConvertAll(myInts, x => x.ToString());
// or using LINQ
// string[] myStrings = myInts.Select(x => x.ToString()).ToArray();
bar = string.Format("{0} and {1}", myStrings);

答案 5 :(得分:0)

你的string.Format期待2个参数({0}和{1})。您只提供1个参数(int [])。你需要更像这样的东西:

string bar = string.Format("{0} and {1}", myInts[0], myInts[1]);

编译器没有注意到该问题,因为格式字符串是在运行时计算的。 IE编译器不知道{0}和{1}意味着应该有2个参数。

答案 6 :(得分:0)

这有效:

string bar = string.Format("{0} and {1}", myInts[0], myInts[1]);

编译器无法捕获它,因为它不会评估您的格式字符串。

你放弃顶部的例子与你在下面尝试做的事情不符...你提供了两个{}和两个参数,但在底部你只提供了一个参数。