web-dev-qa-db-ja.com

int + stringはどのようにしてstringになりますか?

ToString()を実装する奇妙な方法に出くわしましたが、それがどのように機能するのか疑問に思っています。

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

イテレータはcharのサイズを想定していますか?

28
Nabil Lamriben

String.Concat(object, object) メソッドを暗黙的に呼び出し、指定された2つのオブジェクトの文字列表現を連結します。

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

次に、String.Concat(object, object)メソッドはString.Concat(string, string)を呼び出します。 Concatのソースを読み取り、詳細を確認するには、まずここに移動します。 C#.NETのString.csソースコード 次に、そのページでTextBoxを検索します。 Stringと入力し、結果のString.csリンクをクリックして、C#.NETのString.csソースコードページに移動し、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
21
Salah Akbari

この「ステップバイステップ」の説明:

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

したがって、基本的にstringintと連結すると、自動的にint.ToStringメソッドが呼び出され、文字列が結合されます。

15
Mateusz

n--のタイプはintであり、+を使用して""と連結することでstringに変換されます。これはタイプstring。さらに、stringIEnumerable<char>を実装し、その上でforeachによる実際の反復が行われます。

10
Codor

このコードは、私がその言語でひどいデザインを選択した結果であるため、理解できないように見えます。

_+_演算子は、実際にはstringには存在しません。参照ソース、または MSDNページ を見ると、stringに対して宣言されている演算子は_==_と_!=_のみです。

実際に起こることは、コンパイラーがその手品の1つを引き出し、_+_演算子を静的メソッド_string.Concat_の呼び出しに変換することです。

たまたまforeach (char c in string.Concat(n--, ""))に遭遇した場合、意図はclearであるため、コードをよりよく理解できるでしょう。 2つのオブジェクトを文字列として連結し、結果の文字列を構成するcharsを列挙します。

_n-- + ""_を読むと、その意図は明確にはほど遠いものであり、_n-- + s_(sstring)である場合はさらに悪化します。

どちらの場合も、コンパイラは引数を文字列として連結することを決定し、この呼び出しをstring.Concat(object, object)に自動的にマップします。 C#のテナントの1つは、コーダーの意図が明確でない限り、赤い旗を振って、コーダーに彼の意図を明確にするように依頼することです。この特定のケースでは、そのテナントは完全に違反されています。

私見、_string + string_でないものはすべてコンパイル時エラーであるはずですが、その列車は何年も前に通過しました。

6
InBetween

コードを分解して、なぜそれが起こるのかを示すには...

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

オペランドの1つとして文字列を指定して+演算子を使用すると、+が実装されている限り、他のプリミティブオペランドが何であるかに関係なく、結果が文字列に変換されます。そうすることで、次のAutosデバッグウィンドウのスクリーンショットからわかるように、nはstring.Concatメソッドに参加します...

Autos window showing identifier values

ご想像のとおり、「34」でメソッドを呼び出しました。そこでのループ識別子はchar cとして定義され、文字列を通過します。これは、文字列型の「定義に移動」の結果のスクリーンショットからわかるように、stringIEnumerable<char>を実装しているために機能します。

Result of right clicking string type and going to definition

したがって、その時点から、他のリスト/配列を反復処理する場合と同じように機能します...より正確には、foreachでIEnumerableを実行し、個々のcharを取得します。その間にnn-- // 33 in my caseに変更されました。この時点では、式n-- + ""の結果を反復処理しているため、nの値は重要ではありません。 n++だったかもしれませんが、同じ結果が得られます。

s = s + c;はかなり自明である必要があり、そうでない場合に備えて、n-- + ""の一時文字列の結果からプルした各文字が空の(最初の)string s = "";に追加されます。前述のように、文字列が含まれる+は文字列になるため、文字列になります。すべての文字の処理が完了すると、文字列表現が返されます。

1
Fabulous

私はReSharperを使用して関数をLinqステートメントに変換しました。これは、何が起こっているのかを理解するのに役立つ(または単に人々をさらに混乱させる)可能性があります。

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

他の人がすでに述べたように、基本的に入力されたintは空のstringと連結され、基本的にintの文字列表現を提供します。 stringIEnumberableを実装すると、foreachループは文字列をchar[]に分割し、各反復で文字列の各文字を提供します。次に、ループ本体は、各charを連結することにより、文字を文字列として結合します。

したがって、たとえば、5423の入力が与えられると、それは"5423"に変換され、次に"5""4""2""3"に分割され、最後に"5423"にステッチバックされます。

しばらくの間、本当に頭を痛めたのはn--でした。これによりintが減少する場合は、代わりに"5422"が返されませんか? MSDNの記事:インクリメント(++)およびデクリメント(-)演算子 を読むまで、これは私にはわかりませんでした。

インクリメント演算子とデクリメント演算子は、変数に格納されている値を変更してその値にアクセスするためのショートカットとして使用されます。どちらの演算子も、接頭辞または接尾辞の構文で使用できます。

 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の値は、nが1だけデクリメントされる前に、string.Concatによって読み取られて使用されます。

つまりstring.Concat(n--,"")string.Contact(n, ""); n = n - 1;と同じ出力を返します。

したがって、"5422"を取得するには、それをstring.Concat(--n, "")に変更して、nstring.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;
}
0
Adrian
n-- + ""

と同じです

n + ""

後でnの値を使用しないため

n + ""

と同じです

n.ToString()

+演算子をintおよびstringとともに使用すると、intがstringに変換されるため、

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

と同じです

foreach (char c in n.ToString())

残りは簡単です。

0
fdafadf

n--はポストデクリメントであり、nの値は連結後にのみ変更されます。つまり、本質的には何もしていません。それは単にだったかもしれません

foreach (char c in n + "")

または

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

いずれにしても何も変わりません。元の値が繰り返されます

0
Robert Juneau

文字列の連結:

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

バイナリ_+_演算子は、一方または両方のオペランドが文字列型の場合に文字列連結を実行します。文字列連結のオペランドがnullの場合、空の文字列に置き換えられます。それ以外の場合、文字列以外のオペランドは、型ToStringから継承された仮想objectメソッドを呼び出すことによって文字列表現に変換されます。ToStringnullを返す場合、空の文字列に置き換えられます。 — ECMA-334 、201ページ。

したがって、n.ToString()が呼び出されます。メソッド内の他のすべては、結果を分解および再構成するだけで、効果はありません。

次のように書くことができます:

_public string tostr(int n) => n.ToString();
_

しかし、なぜ?

0
Tom Blodget