C# 中Parse 和TryParse的效率問題
阿新 • • 發佈:2019-01-07
我們知道在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;
}
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);
}
}
}
}