web-dev-qa-db-fra.com

Comment int + string devient string?

Je suis tombé sur une étrange façon de mettre en œuvre ToString() et je me demande comment cela fonctionne:

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

L'itérateur prend-il la taille d'une char?

27
Nabil Lamriben

Il appelle implicitement la méthode String.Concat(object, object) , qui concatène les représentations sous forme de chaîne de deux objets spécifiés :

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

La méthode String.Concat(object, object) appelle ensuite String.Concat(string, string). Pour lire le source Concat et le vérifier en profondeur, allez d'abord ici: Code source String.cs en C # .NET puis dans cette page de la recherche TextBox tapez String puis cliquez sur le lien String.cs dans les résultats accédez à la page code source de String.cs en C # .NET et vérifiez la méthode Concat.

C'est la définition de la méthode:

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

Comme vous le voyez, la méthode public static String Concat(String str0, String str1) appelle finalement:

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

Et voici l’IL sous-jacent, par 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
20
S.Akbari

Expliquant ceci "pas à pas":

// 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
}

Donc, fondamentalement, si vous concaténez string avec int, il appellera automatiquement la méthode int.ToString et joindra la chaîne.

15
Mateusz

Le type de n-- est int, qui est converti en string en utilisant + pour le concaténer avec "", qui est du type string. De plus, string implémente IEnumerable<char>, sur lequel l'itération réelle avec foreach a lieu.

10
Codor

Ce code semble incompréhensible parce que c'est le résultat de ce que je considère comme un choix de conception épouvantable dans le langage.

L'opérateur + n'existe pas vraiment dans string. Si vous examinez la source de référence ou la page MSDN , les seuls opérateurs déclarés pour string sont == et !=.

En réalité, le compilateur tire l'un de ses tours de magie et convertit l'opérateur + en un appel à la méthode statique string.Concat.

Maintenant, si vous rencontriez foreach (char c in string.Concat(n--, "")), vous comprendriez probablement mieux le code, car l'intention est clear: je veux concaténer deux objets en tant que chaînes, puis énumérer les chars qui composent la chaîne résultante.

Lorsque vous lisez n-- + "", cette intention est loin d'être claire et encore pire si vous avez n-- + s (s étant string).

Dans les deux cas, le compilateur décide que vous souhaitez concaténer les arguments sous forme de chaînes et mappe automatiquement cet appel à string.Concat(object, object). L'un des locataires de C # est que, à moins que l'intention du codeur ne soit claire, agite un drapeau rouge et demande au codeur de clarifier son intention. Dans ce cas particulier, ce locataire est violé complètement.

IMHO, tout ce qui n'est pas un string + string aurait dû être une erreur de compilation, mais ce train est passé depuis de nombreuses années. 

5
InBetween

Pour décomposer votre code et montrer pourquoi cela se produit ...

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

utiliser l'opérateur + avec une chaîne comme l'un des opérandes convertira le résultat en chaîne, quel que soit ce que fut l'opérande primitif, à condition qu'il ait une implémentation de +. Ce faisant, n participe à la méthode string.Concat comme vous pouvez le voir sur la capture d'écran suivante de la fenêtre de débogage d'Autos ...

 Autos window showing identifier values

J'ai appelé la méthode avec "34" comme vous pouvez en déduire. L'identifiant de la boucle est défini comme char c et traverse une chaîne. Cela fonctionne car string implémente IEnumerable<char> comme vous pouvez le voir sur la capture d'écran du résultat de "Aller à la définition" du type de chaîne:

 Result of right clicking string type and going to definition

Ainsi, à partir de ce moment, cela fonctionne de la même manière que si vous parcouriez une autre liste/un tableau ... ou plus précisément, IEnumerable avec foreach et obteniez chaque individu char. n entre-temps a été remplacé par n-- // 33 in my case. À ce stade, la valeur de n est sans conséquence puisque vous effectuez une itération dans le résultat de l'expression n-- + "". Cela pourrait aussi bien être n++ et vous obtiendriez le même résultat.

La ligne s = s + c; doit être assez explicite et, dans le cas contraire, chaque caractère extrait de la chaîne temporaire résultat n-- + "" est ajouté à votre string s = ""; vide (au début). Il en résulte une chaîne car, comme mentionné précédemment, + avec une chaîne impliquée entraînera une chaîne. Une fois que vous avez terminé de parcourir tous les caractères, la représentation sous forme de chaîne est renvoyée.

1
Fabulous

J'ai utilisé ReSharper pour convertir la fonction en instructions Linq, ce qui peut aider certaines personnes à comprendre ce qui se passe (ou tout simplement dérouter davantage les gens). 

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

Comme d'autres l'ont déjà indiqué, la variable int saisie est concaténée avec une variable string vide qui vous donne essentiellement une représentation sous forme de chaîne de la variable int. Comme string implémente IEnumberable, la boucle foreach divise la chaîne en un char[], donnant chaque caractère de la chaîne à chaque itération. Le corps de la boucle joint alors les caractères en une chaîne en concaténant chaque char.

Ainsi, par exemple, étant donné l'entrée de 5423, il est converti en "5423", puis divisé en "5", "4", "2", "3" et finalement réassemblé en "5423".

Maintenant, la partie qui m'a vraiment fait mal à la tête pendant un moment était le n--. Si cela décrémente la int, pourquoi ne pas renvoyer "5422" à la place? Ce n’était clair pour moi qu’après avoir lu les article MSDN: opérateurs Increment (++) et Decrement (-)

Les opérateurs d'incrémentation et de décrémentation sont utilisés comme raccourci pour modifier la valeur stockée dans une variable et accéder à cette valeur. Soit opérateur peut être utilisé dans une syntaxe préfixe ou postfixe.

 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

Ainsi, étant donné que l'opérateur de décrémentation est appliqué à la fin de n, la valeur de n est lue et utilisée par string.Concat avant que n ne soit décrémenté de 1.

C'est à dire. string.Concat(n--,"") va donner le même résultat que string.Contact(n, ""); n = n - 1;.

Donc, pour obtenir "5422", nous le changerions en string.Concat(--n, "") afin que n soit décrémenté avant d’être passé à string.Contact.

TL; DR; La fonction est une façon de faire n.ToString()

Fait intéressant, j’ai également utilisé ReSharper pour la convertir en une boucle for, mais la fonction ne fonctionne plus car n est décrémenté à chaque itération de la boucle for, contrairement à la boucle 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;
}
0
Adrian
n-- + ""

est le même que

n + ""

parce que nous n'utilisons pas la valeur de n plus tard

n + ""

est le même que 

n.ToString()

parce que l'opérateur + utilisé avec int et string convertit int en string, donc

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

est le même que 

foreach (char c in n.ToString())

le reste est simple.

0
fdafadf

Puisque n-- est un post décrément, la valeur de n n’est modifiée qu’après la concaténation. donc essentiellement, il ne fait rien du tout. ça aurait pu simplement être 

foreach (char c in n + "")

ou

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

de toute façon, rien ne change. la valeur d'origine est itérée 

0
Robert Juneau

Concaténation de chaînes: 

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

L'opérateur binaire + effectue la concaténation de chaînes lorsqu'un ou les deux fichiers les opérandes sont de type chaîne. Si un opérande de concaténation de chaîne est null, une chaîne vide est substituée. Sinon, toute chaîne autre que la chaîne L’opérande est converti en sa représentation sous forme de chaîne en appelant le fichier méthode virtuelle ToString héritée du type object. Si ToString renvoie null, une chaîne vide est substituée . - ECMA-334 , page 201.

Donc, n.ToString() est appelé. Tout le reste de la méthode ne fait que décomposer et recomposer le résultat sans aucun effet.

Cela aurait pu être écrit comme:

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

Mais pourquoi?

0
Tom Blodget