int + string如何成为字符串?

时间:2017-01-30 13:34:57

标签: c# .net obfuscation

我遇到了一种奇怪的方式来实现ToString(),我想知道它是如何工作的:

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

迭代器的大小是char吗?

9 个答案:

答案 0 :(得分:21)

隐式调用String.Concat(object, object)方法,它连接两个指定的对象的字符串表示

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

String.Concat(object, object)方法然后调用String.Concat(string, string)。要阅读Concat的来源并进行深入检查,请先转到此处:String.cs source code in C# .NET,然后在搜索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

答案 1 :(得分:15)

逐步解释这个&#34;&#34; :

// 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方法并将字符串连接在一起。

答案 2 :(得分:10)

n--的类型为int,使用string将其与+连接,后者转换为""string的类型为string 1}}。此外,IEnumerable<char>实现foreach,在{{1}}上进行实际迭代。

答案 3 :(得分:6)

这段代码看起来很难理解,因为它是我认为在语言中设计选择糟糕的结果。

+运算符在string中确实不存在。如果您查看参考来源或MSDN pagestring的唯一声明的运算符为==!=

真正发生的事情是编译器提取了一个神奇的技巧,并将+运算符转换为对静态方法string.Concat的调用。

现在,如果您碰巧遇到foreach (char c in string.Concat(n--, "")),您可能会更好地理解代码,因为意图是 clear :我想将两个对象连接为字符串,然后枚举构成结果字符串的char

当您阅读n-- + ""时,意图远非明确,如果您碰巧n-- + ssstring)则更糟糕。

两种情况下的编译器都决定将参数连接为字符串,并自动将此调用映射到string.Concat(object, object)。 C#的租户之一是,除非编码器的意图明确,否则挥动红旗并要求编码人员澄清他的意图。在这种特殊情况下,该租户完全受到侵犯。

恕我直言,任何不是string + string的事都应该是一个编译时错误,但那列火车很多年前就已经过了。

答案 4 :(得分:1)

分解你的代码以说明它发生的原因......

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

使用带有字符串作为操作数之一的+运算符将结果转换为字符串,而不管其他原始操作数是什么,只要它具有+的实现。在这样做时,n参与string.Concat方法,您可以从以下Autos调试窗口的屏幕截图中看到...

Autos window showing identifier values

我用&#34; 34&#34;你可以推断。其中的循环标识符定义为char c并通过字符串。这是有效的,因为string实现了IEnumerable<char>,正如您可以从&#34的结果屏幕截图中看到的那样;转到定义&#34;字符串类型:

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 = "";。它产生一个字符串,因为如前所述+涉及一个字符串将导致一个字符串。完成所有字符后,它将返回字符串表示。

答案 5 :(得分:0)

我已经使用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",{ {1}}最后缝合回"3"

现在真正伤到我头脑的部分是"5423"。如果这会减少n--,那么为什么我们不会返回int?在我阅读MSDN Article: Increment (++) and Decrement (--) Operators

之后,我才清楚这一点
  

增量和减量运算符用作修改的快捷方式   存储在变量中的值并访问该值。两个运营商   可以用在前缀或后缀语法中。

"5422"

因为递减运算符最后应用于 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减1之前被string.Concat读取并使用。 / p>

即。 n将提供与string.Concat(n--,"")相同的输出。

为了得到string.Contact(n, ""); n = n - 1;,我们会将其更改为"5422",以便string.Concat(--n, "")在传递给n之前先递减。

<强> TL; DR;该功能是关于做string.Contact

的一种方式

有趣的是,我还使用ReSharper将其转换为n.ToString()循环但函数不再起作用for在for循环的每次迭代中递减,与n循环不同:

foreach

答案 6 :(得分:0)

  

字符串连接:

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

二进制+运算符在一个或两个运算符时执行字符串连接   操作数是字符串类型。如果字符串连接的操作数是   null,替换空字符串。否则,任何非字符串   通过调用,将操作数转换为其字符串表示形式   从类型ToString继承的虚拟object方法。如果ToString   返回null,替换空字符串。    - ECMA-334,第201页。

因此,n.ToString()被调用。该方法中的其他所有内容只是分解并重新组合结果,无效。

它可以写成:

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

但是,为什么?

答案 7 :(得分:0)

n-- + ""

相同
n + ""

因为我们以后不会使用n的值

n + ""

相同
n.ToString()

因为+运算符与int和string一起使用将int转换为string,所以

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

相同
foreach (char c in n.ToString())

其余的很简单。

答案 8 :(得分:0)

由于n--是后递减,因此n的值仅在连接后更改。所以基本上它什么都不做。它可能只是

foreach (char c in n + "")

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

无论如何都没有变化。原始值被迭代