Подтвердить что ты не робот

Как int + string становится строкой?

Я натолкнулся на странный способ реализовать ToString(), и мне интересно, как это работает:

public string tostr(int n) 
{
    string s = "";
    foreach (char c in n-- + "") {  //<------HOW IS THIS POSSIBLE ?
        s = s + c;
    }
    return s;
}

Является ли итератором предполагаемый размер a char?

4b9b3361

Ответ 1

Он вызывает неявный метод String.Concat(object, object), который объединяет строковые представления двух указанных объектов:

string result = String.Concat("", n--);

Затем метод String.Concat(object, object) вызывает String.Concat(string, string). Чтобы прочитать источник Concat и проверить его по глубине, сначала перейдите сюда: Исходный код String.cs в С#.NET, а затем на этой странице в теге TextBox введите String, а затем нажмите ссылку String.cs в результатах, чтобы перейти к исходному коду String.cs на странице С#.NET и проверить метод Concat.

Это определение метода:

public static String Concat(Object arg0, Object arg1) 
{ 
    Contract.Ensures(Contract.Result<string>() != null);
    Contract.EndContractBlock(); 

    if (arg0 == null)
    { 
        arg0 = String.Empty;
    }

    if (arg1==null) 
    { 
        arg1 = String.Empty;
    } 
    return Concat(arg0.ToString(), arg1.ToString()); 
}

Как вы видите, это вызывает метод public static String Concat(String str0, String str1) finally:

public static String Concat(String str0, String str1) 
{
    Contract.Ensures(Contract.Result<string>() != null);
    Contract.Ensures(Contract.Result<string>().Length ==
        (str0 == null ? 0 : str0.Length) + 
        (str1 == null ? 0 : str1.Length));
    Contract.EndContractBlock(); 

    if (IsNullOrEmpty(str0)) {
        if (IsNullOrEmpty(str1)) { 
            return String.Empty;
        }
        return str1;
    } 

    if (IsNullOrEmpty(str1)) { 
        return str0; 
    }

    int str0Length = str0.Length;

    String result = FastAllocateString(str0Length + str1.Length);

    FillStringChecked(result, 0,        str0);
    FillStringChecked(result, str0Length, str1); 

    return result;
}

И это базовый IL, Ildasm:

.method public hidebysig instance string 
        tostr(int32 n) cil managed
{
  // Code size       74 (0x4a)
  .maxstack  3
  .locals init ([0] string s,
           [1] string V_1,
           [2] int32 V_2,
           [3] char c,
           [4] string V_4)
  IL_0000:  nop
  IL_0001:  ldstr      ""
  IL_0006:  stloc.0
  IL_0007:  nop
  IL_0008:  ldarg.1
  IL_0009:  dup
  IL_000a:  ldc.i4.1
  IL_000b:  sub
  IL_000c:  starg.s    n
  IL_000e:  box        [mscorlib]System.Int32
  IL_0013:  call       string [mscorlib]System.String::Concat(object)
  IL_0018:  stloc.1
  IL_0019:  ldc.i4.0
  IL_001a:  stloc.2
  IL_001b:  br.s       IL_0039
  IL_001d:  ldloc.1
  IL_001e:  ldloc.2
  IL_001f:  callvirt   instance char [mscorlib]System.String::get_Chars(int32)
  IL_0024:  stloc.3
  IL_0025:  nop
  IL_0026:  ldloc.0
  IL_0027:  ldloca.s   c
  IL_0029:  call       instance string [mscorlib]System.Char::ToString()
  IL_002e:  call       string [mscorlib]System.String::Concat(string,
                                                              string)
  IL_0033:  stloc.0
  IL_0034:  nop
  IL_0035:  ldloc.2
  IL_0036:  ldc.i4.1
  IL_0037:  add
  IL_0038:  stloc.2
  IL_0039:  ldloc.2
  IL_003a:  ldloc.1
  IL_003b:  callvirt   instance int32 [mscorlib]System.String::get_Length()
  IL_0040:  blt.s      IL_001d
  IL_0042:  ldloc.0
  IL_0043:  stloc.s    V_4
  IL_0045:  br.s       IL_0047
  IL_0047:  ldloc.s    V_4
  IL_0049:  ret
}// end of method tostr

Ответ 2

Объяснение этого "шаг за шагом":

// assume the input is 1337
public string tostr(int n) {
    //line below is creating a placeholder for the result string
    string s = "";
    // below line we can split into 2 lines to explain in more detail:
    // foreach (char c in n-- + "") {
    // then value of n is concatenated with an empty string :
    // string numberString = n-- + ""; // numberString is "1337";
    // and after this line value of n will be 1336
    // which then is iterated though :
    // foreach(char c in numberString) { // meaning foreach(char c in "1337")
    foreach (char c in n-- + "") {  //<------HOW IS THIS POSSIBLE ?
        s = s + c; // here each sign of the numberString is added into the placeholder
    }
    return s; // return filled placeholder
}

В принципе, если вы соедините string с int, он автоматически вызовет метод int.ToString и соедините цепочку вместе.

Ответ 3

Тип n-- равен int, который преобразуется в string, используя +, чтобы объединить его с "", который имеет тип string. Кроме того, string реализует IEnumerable<char>, по которому имеет место фактическая итерация с foreach.

Ответ 4

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

Оператор + действительно не существует в string. Если вы посмотрите на источник ссылки или страницу MSDN, единственными объявленными операторами для string являются == и !=.

Что действительно происходит, так это то, что компилятор извлекает один из своих волшебных трюков и преобразует оператор + в вызов статического метода string.Concat.

Теперь, если вы столкнулись с foreach (char c in string.Concat(n--, "")), вы, вероятно, поняли бы код намного лучше, потому что цель понятна: я хочу объединить два объекта в виде строк, а затем перечислить char, которые составляют итоговую строку.

Когда вы читаете n-- + "", это намерение далека от ясности и хуже, если у вас n-- + s (s есть string).

Компилятор в обоих случаях решает, что вы хотите объединить аргументы в виде строк и автоматически сопоставить этот вызов с string.Concat(object, object). Один из арендаторов С# заключается в том, что, если цель кодера не ясна, нарисуйте красный флаг и попросите кодера прояснить его намерение. В этом конкретном случае этот арендатор полностью нарушается.

ИМХО, все, что не является string + string, должно было быть ошибкой времени компиляции, но этот поезд прошел много лет назад.

Ответ 5

Чтобы разбить свой код, чтобы показать, почему это происходит...

foreach(char c in n-- + "")

с помощью оператора + со строкой, так как один из операндов преобразует результат в строку независимо от того, какой был другой примитивный операнд, если у него была реализация+. При этом n участвует в методе string.Concat, как вы можете видеть из следующего снимка экрана отладки Autos...

Окно авто, показывающее значения идентификатора

Я назвал метод с "34", как вы можете сделать вывод. Идентификатор цикла там определен как char c и проходит через строку. Это работает, потому что string реализует IEnumerable<char>, как вы можете видеть на скриншоте результата "Перейти к определению" типа строки:

Результат правого щелчка типа строки и перехода к определению

Итак, с этой точки он работает так же, как если бы вы выполняли итерацию через любой другой список/массив... или точнее, IEnumerable с foreach и получает каждый отдельный char. n в среднем было изменено на n-- // 33 in my case. В этот момент значение n несущественно, поскольку вы выполняете итерацию через результат выражения n-- + "". Это может быть и n++, и вы получите тот же результат.

Строка s = s + c; должна быть довольно понятной и на всякий случай, если это не так, каждый char, который вы извлекаете из временного результата строки n-- + "", добавляется к вашему пусту (в начале) string s = "";, Это приводит к строке, поскольку, как упоминалось ранее, + с вовлеченной строкой приведет к строке. После того, как вы пройдете все символы, он вернет строковое представление.

Ответ 6

Я использовал ReSharper для преобразования функции в выражения Linq, которые могут помочь некоторым людям понять, что происходит (или просто путать людей еще больше).

public string tostrLinq(int n)
{
    return string.Concat(n--, "").Aggregate("", (string current, char c) => current + c);
}

Как уже говорили другие, в основном введенный int конкретизируется с пустым string, который в основном дает вам строковое представление int. Поскольку string реализует IEnumberable, цикл foreach разбивает строку на char[], давая каждому символу строки на каждой итерации. Тело цикла просто присоединяет символы обратно вместе в виде строки, объединяя каждый char.

Так, например, при вводе 5423 он преобразуется в "5423", а затем разбивается на "5", "4", "2", "3" и, наконец, сшивается обратно на "5423".

Теперь часть, которая действительно повредила мне голову, была n--. Если это уменьшает значение int, то почему бы нам не вернуть "5422"? Это мне было непонятно до тех пор, пока я не прочитал Операторы MSDN: Increment (++) и Decrement (-)

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

 If         | Equivalent Action | Return value
=====================================
++variable | variable += 1     | value of variable after incrementing
variable++ | variable += 1     | value of variable before incrementing
--variable | variable -= 1     | value of variable after decrementing
variable-- | variable -= 1     | value of variable before decrementing

Так как оператор декремента применяется в конце до n, значение n считывается и используется string.Concat до n уменьшается на 1.

т.е. string.Concat(n--,"") будет давать тот же результат, что и string.Contact(n, ""); n = n - 1;.

Итак, чтобы получить "5422", мы изменим его на string.Concat(--n, ""), чтобы n уменьшался сначала до того, как он будет передан в string.Contact.

TL; DR; Функция представляет собой круглый способ выполнения n.ToString()

Интересно, что я также использовал ReSharper для преобразования его в цикл for, но функция больше не работает, поскольку n уменьшается на каждой итерации цикла for, в отличие от цикла foreach:

public string tostrFor(int n)
{
    string s = "";
    for (int index = 0; index < string.Concat(n--, "").Length; index++)
    {
        char c = string.Concat(n--, "")[index];
        s = s + c;
    }
    return s;
}

Ответ 7

Конкатенация строк:

…
string operator +(object x, string y);

Двоичный оператор + выполняет конкатенацию строк, когда один или оба операнды имеют тип string. Если операнд конкатенации строк null, заменяется пустая строка. В противном случае любая строка операнд преобразуется в его строковое представление, вызывая virtual ToString, унаследованный от типа object. Если ToStringвозвращает null, заменяется пустая строка. - ECMA-334, стр. 201.

Итак, вызывается n.ToString(). Все остальное в методе просто разлагает и рекомпозирует результат без эффекта.

Это могло быть написано как:

public string tostr(int n) => n.ToString();

Но почему?

Ответ 8

n-- + ""

совпадает с

n + ""

потому что мы не используем значение n позже

n + ""

совпадает с

n.ToString()

потому что оператор +, используемый с int и string, преобразует int в строку, поэтому

foreach (char c in n-- + "")

совпадает с

foreach (char c in n.ToString())

остальное просто.

Ответ 9

Так как n-- является пост-декрементом, значение n изменяется только после конкатенации. так что по существу он ничего не делает. это могло бы быть просто

foreach (char c in n + "")

или

foreach (char c in (n++) + "")

в любом случае ничего не меняется. исходное значение получает итерацию