String类的Format重载方法,用来格式化需要的字符串非常方便。常用的重载方法原型有:
Format(String, Object)
Format(String, Object, Object)
Format(String, Object, Object, Object)
Format(String, Object[])
它们都调用了另外一个重载方法:
Format(IFormatProvider, String, Object[])
在这个方法中,调用了System.Text.StringBuilder类中的AppendFormat方法:
AppendFormat(IFormatProvider, String, Object[])
在这个方法内部对字符串进行了解析,包括我们常常利用大括号”{}”对来匹配变量参数,如:
String.Format(”this is {0}”, UserName);
不知道大家有没有在这个format参数中误用大括号的经历,比如参数本身是段脚本语句,需要使用到大括号,如:
String.Format(”return 1;} function Method{0}(){”, id);
这段代码会抛出FormatException的异常。我们察看Reflector,看看当AppendFormat方法解析这个 “return 1;} function Method{0}(){” 字符串时候,会发生什么情况:
switch (ch1) {
case ‘}’:
if ((num1 < num2) && (chArray1[num1] == ‘}’))
{ num1++; }
else { StringBuilder.FormatError(); }
break;
大概在24行开始,开始判断是否是“}”字符,如果在其后没有紧跟另一个“}”符号,程序立即会抛出异常(通过StringBuilder的FormatError方法)。所以能够解释我们上面所举例子中的情况。但是根据这个反编译的源代码,就不能解释很多正常的情况,比如:
String.Format(”this is {0}”, UserName);
仔细跟踪这个AppendFormat方法,发现都会出现第49行一个数组的调用超出边界,将抛出System.IndexOutOfRangeException:
chArray1[num4++] = ch1;
不知何故,此方法在Framework中能顺利运行,难道是Reflector反编译有误?
以下为反编译的AppendFormat方法代码(真佩服写FCL的牛人,代码逻辑真是深奥阿 ):

AppendFormat
1
public StringBuilder AppendFormat(IFormatProvider provider, string format, params object[] args)
2

{
3
int num3;
4
if ((format == null) || (args == null))
5
{
6
throw new ArgumentNullException((format == null) ? "format" : "args");
7
}
8
char[] chArray1 = format.ToCharArray(0, format.Length);
9
int num1 = 0;
10
int num2 = chArray1.Length;
11
char ch1 = '\0';
12
ICustomFormatter formatter1 = null;
13
if (provider != null)
14
{
15
formatter1 = (ICustomFormatter) provider.GetFormat(typeof(ICustomFormatter));
16
}
17
Label_004E:
18
num3 = num1;
19
int num4 = num1;
20
while (num1 < num2)
21
{
22
ch1 = chArray1[num1];
23
num1++;
24
switch (ch1)
25
{
26
case '}':
27
if ((num1 < num2) && (chArray1[num1] == '}'))
28
{
29
num1++;
30
}
31
else
32
{
33
StringBuilder.FormatError();
34
}
35
break;
36
37
case '{':
38
if ((num1 < num2) && (chArray1[num1] == '{'))
39
{
40
num1++;
41
}
42
else
43
{
44
num1--;
45
break;
46
}
47
break;
48
}
49
chArray1[num4++] = ch1;
50
}
51
if (num4 > num3)
52
{
53
this.Append(chArray1, num3, num4 - num3);
54
}
55
if (num1 == num2)
56
{
57
return this;
58
}
59
num1++;
60
if (((num1 == num2) || ((ch1 = chArray1[num1]) < '0')) || (ch1 > '9'))
61
{
62
StringBuilder.FormatError();
63
}
64
int num5 = 0;
65
do
66
{
67
num5 = ((num5 * 10) + ch1) - 0x30;
68
num1++;
69
if (num1 == num2)
70
{
71
StringBuilder.FormatError();
72
}
73
ch1 = chArray1[num1];
74
}
75
while (((ch1 >= '0') && (ch1 <= '9')) && (num5 < 0xf4240));
76
if (num5 >= args.Length)
77
{
78
throw new FormatException(Environment.GetResourceString("Format_IndexOutOfRange"));
79
}
80
while ((num1 < num2) && ((ch1 = chArray1[num1]) == ' '))
81
{
82
num1++;
83
}
84
bool flag1 = false;
85
int num6 = 0;
86
if (ch1 == ',')
87
{
88
num1++;
89
while ((num1 < num2) && (chArray1[num1] == ' '))
90
{
91
num1++;
92
}
93
if (num1 == num2)
94
{
95
StringBuilder.FormatError();
96
}
97
ch1 = chArray1[num1];
98
if (ch1 == '-')
99
{
100
flag1 = true;
101
num1++;
102
if (num1 == num2)
103
{
104
StringBuilder.FormatError();
105
}
106
ch1 = chArray1[num1];
107
}
108
if ((ch1 < '0') || (ch1 > '9'))
109
{
110
StringBuilder.FormatError();
111
}
112
do
113
{
114
num6 = ((num6 * 10) + ch1) - 0x30;
115
num1++;
116
if (num1 == num2)
117
{
118
StringBuilder.FormatError();
119
}
120
ch1 = chArray1[num1];
121
}
122
while (((ch1 >= '0') && (ch1 <= '9')) && (num6 < 0xf4240));
123
}
124
while ((num1 < num2) && ((ch1 = chArray1[num1]) == ' '))
125
{
126
num1++;
127
}
128
object obj1 = args[num5];
129
string text1 = null;
130
if (ch1 != ':')
131
{
132
goto Label_0253;
133
}
134
num1++;
135
num3 = num1;
136
num4 = num1;
137
Label_01E8:
138
if (num1 == num2)
139
{
140
StringBuilder.FormatError();
141
}
142
ch1 = chArray1[num1];
143
num1++;
144
switch (ch1)
145
{
146
case '{':
147
if ((num1 < num2) && (chArray1[num1] == '{'))
148
{
149
num1++;
150
goto Label_0232;
151
}
152
StringBuilder.FormatError();
153
goto Label_0232;
154
155
case '}':
156
if ((num1 < num2) && (chArray1[num1] == '}'))
157
{
158
num1++;
159
}
160
else
161
{
162
num1--;
163
if (num4 > num3)
164
{
165
text1 = new string(chArray1, num3, num4 - num3);
166
}
167
goto Label_0253;
168
}
169
break;
170
}
171
Label_0232:
172
chArray1[num4++] = ch1;
173
goto Label_01E8;
174
Label_0253:
175
if (ch1 != '}')
176
{
177
StringBuilder.FormatError();
178
}
179
num1++;
180
string text2 = null;
181
if (formatter1 != null)
182
{
183
text2 = formatter1.Format(text1, obj1, provider);
184
}
185
if (text2 == null)
186
{
187
if (obj1 is IFormattable)
188
{
189
text2 = ((IFormattable) obj1).ToString(text1, provider);
190
}
191
else if (obj1 != null)
192
{
193
text2 = obj1.ToString();
194
}
195
}
196
if (text2 == null)
197
{
198
text2 = string.Empty;
199
}
200
int num7 = num6 - text2.Length;
201
if (!flag1 && (num7 > 0))
202
{
203
this.Append(' ', num7);
204
}
205
this.Append(text2);
206
if (flag1 && (num7 > 0))
207
{
208
this.Append(' ', num7);
209
}
210
goto Label_004E;
211
}
212
213
214
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· [AI/GPT/综述] AI Agent的设计模式综述