2017-01-30 20 views
29

я наткнулся странным образом реализовать ToString() и мне интересно, как это работает:Как int + string становится строкой?

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

ли итератор предполагая размер char?

+17

Надеюсь, это не производственный код ... –

+0

Попробуйте это http://stackoverflow.com/questions/17575375/how-do-i-convert-an-int-to-a-string -in-c-sharp-without-use-tostring –

+0

Это общий трюк Code Golf, он не должен использоваться в производственном коде, хотя, как вы видели, это приводит к путанице. – TheLethalCoder

ответ

21

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

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

Метод String.Concat(object, object) затем вызывает String.Concat(string, string). Чтобы прочитать источник Concat и проверить его по глубине, сначала перейдите сюда: , а затем на этой странице в поиске TextBox введите String, а затем нажмите на ссылку String.cs в результатах поиска, чтобы перейти к String.cs исходный код в C# .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) метод наконец:

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

Я всегда считал «волшебство» за строковым оператором '+' плохим дизайном. Оператор действительно не существует, компилятор делает какую-то магию и называет 'Concat'. Я нахожу, что это далеко не интуитивно понятно, что '1 + s' должен неявно преобразовывать число в строку, это любой простой pifall, чтобы впасть, особенно для начинающих программистов. 'string.Concat (1, s)' должен был быть единственным вариантом, и все, что не является 'string + string', должно было быть ошибкой времени компиляции. – InBetween

+0

@InBetween Что означает, что оператор «существует»? Если '1 + 1' называется (гипотетически)' Number.Add (1, 1) ', это будет плохой дизайн? – immibis

+0

@immibis означает, что в 'string' нет оператора' + '.Компилятор создает специальный код для конкатенации строк через '+', это не перегрузка, реализованная в классе. – InBetween

10

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

15

Объясняя это «шаг за шагом»:

// 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 метод и присоединиться строку вместе.

+6

Примечание. 'N -' является пост декрементом, поэтому 'n' преобразуется в строку с символом n +" "', а затем уменьшается. – TheLethalCoder

+0

@ TheLethalCoder извините, моя ошибка. Исправлено это в обновленном ответе. –

5

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

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

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

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

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

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

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

+2

Назад в старые добрые времена - «Золотая эра» компьютеров - страшно означало удивительный. –

+0

Есть еще один шаг, который вы устраняете, это то, что оба параметра Concat вызывают их ToString, поэтому n-- станет (n -). ToString() –

+1

Стоит отметить, что компилятор объединяет длинную серию операций «+» по строкам в один вызов Concat. Предположительно, команда разработчиков увидела, что это был очень распространенный способ разработчиков, строящих строки. Если бы он был реализован в качестве стандартного оператора, ему необходимо было бы выполнять каждый Concat отдельно, что немного расточительно, так как оно создает кучу промежуточных экземпляров строк. Возможно, он больше не должен существовать из-за интерполяции строк, но теперь он будет использоваться для обратной совместимости. –

0

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

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

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

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

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

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

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 декрементируется на каждой итерации для цикла, в отличие от цикла 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; 
} 
1

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

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

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

Autos window showing identifier values

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

Result of right clicking string type and going to definition

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

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

+0

Часть о 'char', которая неявно конвертируется в' int', здесь совершенно неактуальна. Код преобразует 'int' в' string', а затем выполняет итерации над символами ... ни в коем случае не подразумевается, что какой-либо символ '' неявно конвертируется в 'int'. Другие ответы более ясны, ИМО. –

+0

@JonSkeet благодарит за отзыв. Я удалил раздел на char/int. Я думал, что это было бы полезно, так как я также собирался рассказать о том, что строка является 'IEnumerable ' – Fabulous

0

Объединение строк:

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

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

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

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

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

Но почему?

0
n-- + "" 

такая же, как

n + "" 

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

n + "" 

такая же, как

n.ToString() 

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

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

такая же, как

foreach (char c in n.ToString()) 

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

0

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

foreach (char c in n + "") 

или

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

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