我们知道在C#中数值类型中转换有Parse和TryParse两个方法,两个最大的区别是,如果字符串不满足转换要求,Parse方法将会引发一个异常;TryParse方法则不会引发异常,它会返回fasle,同时将传入的值置为0。
下面我用测试用例来验证两者的效率问题:
测试转换成功的效率问题:
同过上面的图片可以看到 在转换成功时,效率方面TryParse 略微占了上风。
测试转换失败效率的问题
通过这个测试可以发现TryParse的效率明显高于Parse,当然读者还可以测试其它类型的转换。
可是为什么TryParse的效率比Parse的效率高呢?建议读者看看源码就明白了。
微软提供的代码如下:
Parse 相关代码如下:
[Pure]
public static int Parse(String s) {
return Number.ParseInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
}
[System.Security.SecuritySafeCritical] // auto-generated
internal unsafe static Int32 ParseInt32(String s, NumberStyles style, NumberFormatInfo info) {
Byte * numberBufferBytes = stackalloc Byte[NumberBuffer.NumberBufferBytes];
NumberBuffer number = new NumberBuffer(numberBufferBytes);
Int32 i = 0;
StringToNumber(s, style, ref number, info, false);
if ((style & NumberStyles.AllowHexSpecifier) != 0) {
if (!HexNumberToInt32(ref number, ref i)) {
throw new OverflowException(Environment.GetResourceString("Overflow_Int32"));
}
}
else {
if (!NumberToInt32(ref number, ref i)) {
throw new OverflowException(Environment.GetResourceString("Overflow_Int32"));
}
}
return i;
}
[System.Security.SecuritySafeCritical] // auto-generated
private unsafe static void StringToNumber(String str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo info, Boolean parseDecimal) {
if (str == null) {
throw new ArgumentNullException("String");
}
Contract.EndContractBlock();
Contract.Assert(info != null, "");
fixed (char* stringPointer = str) {
char * p = stringPointer;
if (!ParseNumber(ref p, options, ref number, null, info , parseDecimal)
|| (p - stringPointer < str.Length && !TrailingZeros(str, (int)(p - stringPointer)))) {
throw new FormatException(Environment.GetResourceString("Format_InvalidString"));
}
}
}
private static Boolean HexNumberToInt32(ref NumberBuffer number, ref Int32 value) {
UInt32 passedValue = 0;
Boolean returnValue = HexNumberToUInt32(ref number, ref passedValue);
value = (Int32)passedValue;
return returnValue;
}
[System.Security.SecuritySafeCritical] // auto-generated
private unsafe static Boolean HexNumberToUInt32(ref NumberBuffer number, ref UInt32 value) {
Int32 i = number.scale;
if (i > UInt32Precision || i < number.precision) {
return false;
}
Char* p = number.digits;
Contract.Assert(p != null, "");
UInt32 n = 0;
while (--i >= 0) {
if (n > ((UInt32)0xFFFFFFFF / 16)) {
return false;
}
n *= 16;
if (*p != '\0') {
UInt32 newN = n;
if (*p != '\0') {
if (*p >= '0' && *p <= '9') {
newN += (UInt32)(*p - '0');
}
else {
if (*p >= 'A' && *p <= 'F') {
newN += (UInt32)((*p - 'A') + 10);
}
else {
Contract.Assert(*p >= 'a' && *p <= 'f', "");
newN += (UInt32)((*p - 'a') + 10);
}
}
p++;
}
// Detect an overflow here...
if (newN < n) {
return false;
}
n = newN;
}
}
value = n;
return true;
}
TryParse的相关代码
[Pure]
public static bool TryParse(String s, out Int32 result) {
return Number.TryParseInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
}
[System.Security.SecuritySafeCritical] // auto-generated
internal unsafe static Boolean TryParseInt32(String s, NumberStyles style, NumberFormatInfo info, out Int32 result) {
Byte * numberBufferBytes = stackalloc Byte[NumberBuffer.NumberBufferBytes];
NumberBuffer number = new NumberBuffer(numberBufferBytes);
result = 0;
if (!TryStringToNumber(s, style, ref number, info, false)) {
return false;
}
if ((style & NumberStyles.AllowHexSpecifier) != 0) {
if (!HexNumberToInt32(ref number, ref result)) {
return false;
}
}
else {
if (!NumberToInt32(ref number, ref result)) {
return false;
}
}
return true;
}
internal static Boolean TryStringToNumber(String str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo numfmt, Boolean parseDecimal) {
return TryStringToNumber(str, options, ref number, null, numfmt, parseDecimal);
}
[System.Security.SecuritySafeCritical] // auto-generated
[System.Runtime.CompilerServices.FriendAccessAllowed]
internal unsafe static Boolean TryStringToNumber(String str, NumberStyles options, ref NumberBuffer number, StringBuilder sb, NumberFormatInfo numfmt, Boolean parseDecimal) {
if (str == null) {
return false;
}
Contract.Assert(numfmt != null, "");
fixed (char* stringPointer = str) {
char * p = stringPointer;
if (!ParseNumber(ref p, options, ref number, sb, numfmt, parseDecimal)
|| (p - stringPointer < str.Length && !TrailingZeros(str, (int)(p - stringPointer)))) {
return false;
}
}
return true;
}
完整的源代码如下:
https://referencesource.microsoft.com/#mscorlib/system/int32.cs,225942ed7b7a3252
在打开的网页中直接搜索Parse和TryParse ,便看到对应的源码了。
相关测试的代码如下:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EfficietionProject
{
class Program
{
static string val = "123a";
static void Main(string[] args)
{
int counter = 1;
Console.Write("输入测试的数量:");
string line = null;
while ((line = Console.ReadLine()) != null)
{
if (!int.TryParse(line, out counter))
{
counter = 1;
}
Stopwatch sw = Stopwatch.StartNew();
Parse(counter);
sw.Stop();
Console.WriteLine("Parse:" + sw.ElapsedMilliseconds.ToString());
sw.Restart();
TryParse(counter);
sw.Stop();
Console.WriteLine("TryParse:" + sw.ElapsedMilliseconds.ToString());
Console.Write("输入测试的数量:");
}
}
static void Parse(int counter)
{
int res = 0;
for (int i = 0; i < counter; i++)
{
try
{
res = int.Parse(val);
}
catch
{
res = 0;
}
}
}
static void TryParse(int counter)
{
int res = 0;
for (int i = 0; i < counter; i++)
{
int.TryParse(val, out res);
}
}
}
}