本文的项目环境为 .net 6.0 (.net 5.0 以上都支持)
在 .net 中获取字符串的 MD5 相信是非常容易的事情吧, 但是随便在网上搜一搜发现流传的版本还不少呢,比如:
-
StringBuilder
版本(应该算是官方版本了,使用的人最多,我发现在 ABP 中也是使用的这个) -
BitConverter
版本 -
StringConcat
版本 (字符串拼接,用的人很少,估计都知道性能不好)
但是它们是否是最佳实现? 我们来测试一下
StringBuilder 版本
public static string Md5_StringBuilder(string input)
{
using var md5 = MD5.Create();
var inputBytes = Encoding.UTF8.GetBytes(input);
var hashBytes = md5.ComputeHash(inputBytes);
var sb = new StringBuilder();
foreach (var hashByte in hashBytes)
{
sb.Append(hashByte.ToString("X2"));
}
return sb.ToString();
}
BitConverter 版本
public static string Md5_BitConverter(string input)
{
using var md5 = MD5.Create();
var inputBytes = Encoding.UTF8.GetBytes(input);
var hashBytes = md5.ComputeHash(inputBytes);
return BitConverter.ToString(hashBytes).Replace("-", "");
}
StringConcat 版本
public static string Md5_StringConcat(string input)
{
using var md5 = MD5.Create();
var inputBytes = Encoding.UTF8.GetBytes(input);
var hashBytes = md5.ComputeHash(inputBytes);
var output = string.Empty;
foreach (var hashByte in hashBytes)
{
output += hashByte.ToString("X2");
}
return output;
}
性能对比
先上我测试得到的数据(本机配置: 4 核 8 线程, 测试结果可能不一致)
看结果,的确是字符串拼接性能最差,但是
StringBuilder
好像也不是很高效啊,那个什么 Static 是啥玩意,怎么性能这么好,相对于 StringBuilder, 单线程性能提高了 3 倍, 多线性提高了 5 倍???
没错,这就是我要说的, 从 .net 5.0 开始提供了 2 个非常高效的方法
Convert.ToHexString
MD5.HashData
Convert.ToHexString 实例版本
public static string MD5_HexConvert_Instance(string input)
{
using var md5 = MD5.Create();
var inputBytes = Encoding.UTF8.GetBytes(input);
var hashBytes = md5.ComputeHash(inputBytes);
return Convert.ToHexString(hashBytes);
}
MD5.HashData 静态版本(强烈建议)
public static string MD5_HexConvert_Static(string input)
{
var inputBytes = Encoding.UTF8.GetBytes(input);
var hashBytes = MD5.HashData(inputBytes);
return Convert.ToHexString(hashBytes);
}
总结
-
强烈建议 使用
MD5.HashData
+Convert.ToHexString
, 代码性能最高,也最简洁,只有 3 行 -
一定不要 忘记释放 MD5,我看网上很多在使用实例版本
MD5.Create()
后都没有 Dispose, 这会导致 内存泄漏!!!
最后放上我的完整的测试代码
using System.Text;
using System.Security.Cryptography;
using System.Diagnostics;
namespace ConsoleTest;
class Program
{
public static string Md5_StringBuilder(string input)
{
using var md5 = MD5.Create();
var inputBytes = Encoding.UTF8.GetBytes(input);
var hashBytes = md5.ComputeHash(inputBytes);
var sb = new StringBuilder();
foreach (var hashByte in hashBytes)
{
sb.Append(hashByte.ToString("X2"));
}
return sb.ToString();
}
public static string Md5_StringConcat(string input)
{
using var md5 = MD5.Create();
var inputBytes = Encoding.UTF8.GetBytes(input);
var hashBytes = md5.ComputeHash(inputBytes);
var output = string.Empty;
foreach (var hashByte in hashBytes)
{
output += hashByte.ToString("X2");
}
return output;
}
public static string Md5_BitConverter(string input)
{
using var md5 = MD5.Create();
var inputBytes = Encoding.UTF8.GetBytes(input);
var hashBytes = md5.ComputeHash(inputBytes);
return BitConverter.ToString(hashBytes).Replace("-", "");
}
public static string MD5_HexConvert_Instance(string input)
{
using var md5 = MD5.Create();
var inputBytes = Encoding.UTF8.GetBytes(input);
var hashBytes = md5.ComputeHash(inputBytes);
return Convert.ToHexString(hashBytes);
}
public static string MD5_HexConvert_Static(string input)
{
var inputBytes = Encoding.UTF8.GetBytes(input);
var hashBytes = MD5.HashData(inputBytes);
return Convert.ToHexString(hashBytes);
}
private const int LOOPCOUNT = 100_0000;
private static void CalcTime(Func<string, string> func, bool parallel = true)
{
Stopwatch stopwatch = Stopwatch.StartNew();
if (parallel)
{
Parallel.For(0, LOOPCOUNT, i =>
{
func("123456");
});
}
else
{
for (int i = 0; i < LOOPCOUNT; i++)
{
func("123456");
}
}
stopwatch.Stop();
Console.WriteLine($"{func.Method.Name,32}: {stopwatch.ElapsedMilliseconds,4}ms");
}
static void Main()
{
Test();
Console.ReadKey();
}
private static void Test()
{
var functions = new Liststring, string>>
{
Md5_StringConcat,
Md5_StringBuilder,
Md5_BitConverter,
MD5_HexConvert_Instance,
MD5_HexConvert_Static,
};
foreach (var func in functions)
{
Console.WriteLine($"{func.Method.Name,32}: {func("123456")}");
}
Console.WriteLine($"\n单线程 {LOOPCOUNT} 次用时统计:");
foreach (var func in functions)
{
CalcTime(func, false);
}
Console.WriteLine($"\n多线程 {LOOPCOUNT} 次用时统计:");
foreach (var func in functions)
{
CalcTime(func, true);
}
}
}