string.Format не выполняется во время выполнения с массивом целых чисел - PullRequest
22 голосов
/ 15 июля 2010

Рассмотрим 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);

Индекс (на основе нуля) должен быть больше или равен нулю и меньше размера списка аргументов.

  • Почему массив int не может быть приведен или помещен в object[] или string[]?
  • Из небольшого любопытства, почему компилятор не улавливает это?

Ответы [ 7 ]

25 голосов
/ 15 июля 2010

Вызов завершается неудачно по той же причине, что следующее также не будет выполнено:

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, которая принимает object[].

3 голосов
/ 15 июля 2010

Ваш звонок переводится на:

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

, что приводит к этой строке:

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

Так как {1} не имеет параметра, он генерирует исключение

2 голосов
/ 15 июля 2010

Я думаю, что у вас возникла проблема в том, почему int[] не приведен к object[]. Вот пример, который показывает, почему это будет плохо

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

Проблема в том, что мы просто добавили объект в массив int.

Итак, что происходит в вашем коде, это то, что myInts приводится к object, и у вас нет второго аргумента для заполнения {1}

1 голос
/ 11 марта 2019

Короткий способ заставить его работать (хотя и не самый оптимальный):

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);
1 голос
/ 08 мая 2018

Это довольно старый вопрос, но недавно я получил ту же проблему.И я не видел ответа, который мне подходит, поэтому поделюсь найденным решением.

  • Почему массив int не может быть приведен или помещен в объект [] или строку []? Почему это не в штучной упаковке, я не знаю.Но это может быть упаковано явно, см. Решение ниже.
  • Почему компилятор не улавливает это? Поскольку компилятор неправильно интерпретирует ситуацию: тип не является точно массивом объектов, поэтому он не знает, что с ним делать, и решает выполнить .ToString() для массива int, который возвращает один единственный параметр, содержащий имя типаа не сам список параметров.Он не делает этого со строковым массивом, потому что целевой тип уже является строкой - но с любым другим видом массива возникает такая же проблема (например, bool[]).Рассмотрим var arr1 = new int[]{1,2}; с string.Format("{0}", arr1): если в строке формата указано только {0}, вы получите только имя типа "System.Int32[]" (без исключений).Если у вас есть больше заполнителей, например, string.Format("{0}{1}", arr1), то возникает исключение - потому что arr1 неверно истолкован как параметр one - а для компилятора, a 2 nd one отсутствует.Но я думаю, что концептуальная ошибка в том, что вы не можете конвертировать 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 и параметр для 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());
0 голосов
/ 15 июля 2010

Это работает:

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

Компилятор не перехватывает его, потому что он не оценивает вашу строку формата.

Пример, который вы выдвинули сверху, не соответствует тому, что вы пытаетесь сделать ниже ... вы предоставили два {} и два аргумента, но в нижнем вы предоставили только один аргумент.

0 голосов
/ 15 июля 2010

Ваша строка. Формат ожидает 2 аргумента ({0} и {1}). Вы предоставляете только 1 аргумент (int []). Вам нужно что-то вроде этого:

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

Компилятор не замечает проблему, потому что строка формата вычисляется во время выполнения. IE Компилятор не знает, что {0} и {1} означают, что должно быть 2 аргумента.

...