什么时候应该实现String到值类型的转换器
.Net内部的值类型都提供Parse用来处理string的转换,相信在托管代码下这样转换应该是最高效的了;看上去自己实现一个string的转换对象似乎没有这个必要。但Parse方法有个缺点,除非能保证提供的string有效性要不就会发异常。有时为了保证转换错误不会影响程序会这样做:
try
{
value = Int16.Parse(str[i]);
}
catch
{}
以上代码看去并没有什么问题,但在某些场景下就是问题的根源!
有时候实现一个对象数据绑定器,数据来源于Web的提交信息。通过绑定功能可以节省大量手写代码:
Codes.OrderSearch search = BindObject<Codes.OrderSearch>("");
由于Web提供的信息都是基于String,因为通过反射根据类型进行转换;为了保证个别成员的转换错误不影响整个过程那只有加上try{}catch{}。因为组件内部是不能保证提供的String合法性,因此在转换过程抛出异常是经常的事;就是因为异常的抛出会导致BindObject的过程中性能会出现严重问题。
为了证实以上问题写了一个小小的测试用例:
static void
{
string[] str = new string[] {"2123","234s12","12123","4325","12er243","-4112" ,"3rer32","-1213","121","22342313"};
Int16 value = -1;
object converobj;
IStringConverter toint = new ToInt16();
bool ok;
Stopwatch watch = new System.Diagnostics.Stopwatch();
for (int i = 0; i < 10; i++)
{
watch.Reset();
watch.Start();
try
{
value = Int16.Parse(str[i]);
}
catch
{
value = -1;
}
watch.Stop();
Console.WriteLine("value:{0}", value!=-1?(object)value:"Error");
Console.WriteLine("System Pares Time:{0}", watch.Elapsed);
watch.Reset();
watch.Start();
converobj = toint.ConvertTo(str[i], out ok);
if (ok)
value = (Int16)converobj;
else
value = -1;
watch.Stop();
Console.WriteLine("value:{0}", value !=-1 ? (object)value : "Error");
Console.WriteLine("Custom Pares Time:{0}", watch.Elapsed);
Console.WriteLine("====================================");
decimal de = decimal.Parse("1212.45");
}
}
下页结果才能看到问题原因:
value:2123
System Pares Time:00:00:00.0000167
value:2123
Custom Pares Time:00:00:00.0014842
====================================
value:Error
System Pares Time:00:00:00.0046785
value:Error
Custom Pares Time:00:00:00.0000388
====================================
value:12123
System Pares Time:00:00:00.0000148
value:12123
Custom Pares Time:00:00:00.0000785
====================================
value:4325
System Pares Time:00:00:00.0000139
value:4325
Custom Pares Time:00:00:00.0000145
====================================
value:Error
System Pares Time:00:00:00.0050481
value:Error
Custom Pares Time:00:00:00.0000156
====================================
value:-4112
System Pares Time:00:00:00.0000153
value:-4112
Custom Pares Time:00:00:00.0000153
====================================
value:Error
System Pares Time:00:00:00.0037747
value:Error
Custom Pares Time:00:00:00.0000164
====================================
value:-1213
System Pares Time:00:00:00.0000148
value:-1213
Custom Pares Time:00:00:00.0000156
====================================
value:121
System Pares Time:00:00:00.0000142
value:121
Custom Pares Time:00:00:00.0000148
====================================
value:Error
System Pares Time:00:00:00.0034585
value:Error
Custom Pares Time:00:00:00.0000159
====================================
从结果可以看到在转换错误时所负出的代价。
因此在某些场景下使用自定义转换类还是很有必要的。
ToInt16的实现代码:
public class ToInt16 : IStringConverter
{
#region IStringConverter 成员
object IStringConverter.ConvertTo(string value, out bool succeeded)
{
succeeded = (value != null && value != "");
if (!succeeded)
return null;
int count;
char[] chars = value.ToCharArray();
count = chars.Length;
if (count > 6)
{
succeeded = false;
return null;
}
if (count == 6 && chars[0] != '-')
{
succeeded = false;
return null;
}
for (int i = 0; i < count; i++)
{
succeeded = char.IsNumber(chars[i]) || (chars[i] == '-' && i == 0);
if (!succeeded)
return null;
}
if (chars[0] == '-')
{
if (count == 6)
{
succeeded = char.GetNumericValue(chars[1]) <= 3 &&
char.GetNumericValue(chars[2]) <= 2 &&
char.GetNumericValue(chars[3]) <= 7 &&
char.GetNumericValue(chars[4]) <= 6 &&
char.GetNumericValue(chars[5]) <= 8;
}
}
else
{
if (count == 5)
{
succeeded = char.GetNumericValue(chars[0]) <= 3 &&
char.GetNumericValue(chars[1]) <= 2 &&
char.GetNumericValue(chars[2]) <= 7 &&
char.GetNumericValue(chars[3]) <= 6 &&
char.GetNumericValue(chars[4]) <= 7;
}
}
if (succeeded)
return Int16.Parse(value);
return null;
}
#endregion
}