我遇到了一种奇怪的方式来实现ToString()
,我想知道它是如何工作的:
public string tostr(int n)
{
string s = "";
foreach (char c in n-- + "") { //<------HOW IS THIS POSSIBLE ?
s = s + c;
}
return s;
}
迭代器的大小是char
吗?
答案 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
}
所以基本上如果你将string
与int
连接起来,它会自动调用int.ToString
方法并将字符串连接在一起。
答案 2 :(得分:10)
n--
的类型为int
,使用string
将其与+
连接,后者转换为""
,string
的类型为string
1}}。此外,IEnumerable<char>
实现foreach
,在{{1}}上进行实际迭代。
答案 3 :(得分:6)
这段代码看起来很难理解,因为它是我认为在语言中设计选择糟糕的结果。
+
运算符在string
中确实不存在。如果您查看参考来源或MSDN page,string
的唯一声明的运算符为==
和!=
。
真正发生的事情是编译器提取了一个神奇的技巧,并将+
运算符转换为对静态方法string.Concat
的调用。
现在,如果您碰巧遇到foreach (char c in string.Concat(n--, ""))
,您可能会更好地理解代码,因为意图是 clear :我想将两个对象连接为字符串,然后枚举构成结果字符串的char
。
当您阅读n-- + ""
时,意图远非明确,如果您碰巧n-- + s
(s
为string
)则更糟糕。
两种情况下的编译器都决定将参数连接为字符串,并自动将此调用映射到string.Concat(object, object)
。 C#的租户之一是,除非编码器的意图明确,否则挥动红旗并要求编码人员澄清他的意图。在这种特殊情况下,该租户完全受到侵犯。
恕我直言,任何不是string + string
的事都应该是一个编译时错误,但那列火车很多年前就已经过了。
答案 4 :(得分:1)
分解你的代码以说明它发生的原因......
foreach(char c in n-- + "")
使用带有字符串作为操作数之一的+
运算符将结果转换为字符串,而不管其他原始操作数是什么,只要它具有+的实现。在这样做时,n参与string.Concat
方法,您可以从以下Autos调试窗口的屏幕截图中看到...
我用&#34; 34&#34;你可以推断。其中的循环标识符定义为char c
并通过字符串。这是有效的,因为string
实现了IEnumerable<char>
,正如您可以从&#34的结果屏幕截图中看到的那样;转到定义&#34;字符串类型:
从那时起,它就像你在迭代任何其他列表/数组一样......或者更确切地说,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++) + "")
无论如何都没有变化。原始值被迭代