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![]()