目录
介绍
背景
哈希助手类
考虑
对象到哈希字符串的过程
多个对象组合的哈希
我们将更频繁地使用的方法
整个对象的哈希
重要的是记住
数据值散列
哈希结果
其他测试
奖励:字符串哈希
结论
散列是将值转换为通常较短的固定长度的键/值,其表示原始值。几天前,我们不得不使用哈希比较来通过API在两个系统之间同步数据(显然,这不是使用API进行数据同步的最有效方式,但我们没有选择在源端添加任何更改) 。
我们在做什么:
一切都按预期工作,直到我们重构现有代码(更改了一些模型和属性的名称)。哈希字符串是从整个对象(包括所有值)生成的,而不是考虑特定的属性。我们创建哈希字符串的方式实际上是错误的。我们来看几个哈希字符串示例。
这是用于管理与哈希相关的操作的实用程序类。
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Cryptography;
using System.Text;
public class HashHelper
{
///
/// for custom class need [Serializable]
/// to ignore https://stackoverflow.com/questions/33489930/
/// ignore-non-serialized-property-in-binaryformatter-serialization
///
///
///
public byte[] Byte(object value)
{
/*https://stackoverflow.com/questions/1446547/
how-to-convert-an-object-to-a-byte-array-in-c-sharp*/
using (var ms = new MemoryStream())
{
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(ms, value == null ? "null" : value);
return ms.ToArray();
}
}
public byte[] Hash(byte[] value)
{
/*https://support.microsoft.com/en-za/help/307020/
how-to-compute-and-compare-hash-values-by-using-visual-cs*/
/*https://andrewlock.net/why-is-string-gethashcode-
different-each-time-i-run-my-program-in-net-core*/
byte[] result = MD5.Create().ComputeHash(value);
return result;
}
public byte[] Combine(params byte[][] values)
{
/*https://stackoverflow.com/questions/415291/
best-way-to-combine-two-or-more-byte-arrays-in-c-sharp*/
byte[] rv = new byte[values.Sum(a => a.Length)];
int offset = 0;
foreach (byte[] array in values)
{
System.Buffer.BlockCopy(array, 0, rv, offset, array.Length);
offset += array.Length;
}
return rv;
}
public string String(byte[] hash)
{
/*https://stackoverflow.com/questions/1300890/
md5-hash-with-salt-for-keeping-password-in-db-in-c-sharp*/
StringBuilder sb = new StringBuilder();
for (int i = 0; i < hash.Length; i++)
{
sb.Append(hash[i].ToString("x2")); /*do not make it X2*/
}
var result = sb.ToString();
return result;
}
public byte[] Hash(params object[] values)
{
byte[][] bytes = new byte[values.Length][];
for(int i=0; i < values.Length; i++)
{
bytes[i] = Byte(values[i]);
}
byte[] combined = Combine(bytes);
byte[] combinedHash = Hash(combined);
return combinedHash;
}
/*https://stackoverflow.com/questions/5868438/c-sharp-generate-a-random-md5-hash*/
public string HashString(string value, Encoding encoding = null)
{
if (encoding == null)
{
encoding = Encoding.ASCII;
}
byte[] bytes = encoding.GetBytes(value);
byte[] hash = Hash(bytes);
string result = String(hash);
return result;
}
public string HashString(params object[] values)
{
var hash = Hash(values); /*Add more not constant properties as needed*/
var value = String(hash);
return value;
}
}
或者:
数据类或模型:
[Serializable]
class PeopleModel
{
public long? Id { get; set; }
public string Name { get; set; }
public bool? IsActive { get; set; }
public DateTime? CreatedDateTime { get; set; }
}
创建模型的哈希:
/*9105d073ad276d742c56a049abd4ddef
* will change if we change
* 1. class name
* 2. property name
* 3. property data type
* 4. add/remove new property etc
*/
var peopleModelHashString = hashHelper.HashString(new PeopleModel()
{
Id = 1,
Name = "Anders Hejlsberg",
IsActive = true,
CreatedDateTime = new DateTime(1960, 12, 2)
});
此哈希取决于对象结构和分配的值。即使我们为属性分配相同的值,生成的哈希也不会相同,但添加了一些更改,如:
到模型。而在开发环境中,重构可能发生的任何时间。
让我们只使用值进行哈希。创建一个接口IHash。
public interface IHash
{
string HashString();
}
使用IHash到模型并在方法HashString()内部使用哈希助手。
class People : IHash
{
public long? Id { get; set; } /*unique identifier, avoid it to use
in hash calculation*/
public string Name { get; set; }
public bool? IsActive { get; set; }
public DateTime? CreatedDateTime { get; set; }
public string HashString()
{
var value = new HashHelper().HashString
(Name, IsActive, CreatedDateTime); /*Add more not constant properties as needed*/
return value;
}
}
这样,模型结构不参与哈希生成过程中,只有特定的属性值(Name,IsActive,CreatedDateTime)正被考虑。
Hash将保持不变,直到没有为这些属性设置任何新值。模型的任何结构更改(名称更改,属性添加/删除等)都不会影响哈希字符串。
/*constant: 3953fbec5b81ccca72c98655c0c4b069*/
people = new People()
{
Id = 1,
Name = "Dennis Ritchie",
IsActive = false,
CreatedDateTime = new DateTime(1941, 9, 9)
};
hashString = people.HashString();
使用null 对象值正常工作:
string hashString;
/*constant: 47ccecfc14f9ed9eff5de591b8614077*/
var people = new People();
hashString = people.HashString();
我们将无法创建整个People类,因为它不使用[Serializable]:
var hashHelper = new HashHelper();
/*throws error as [Serializable] not been used*/
//var peopleHashString = hashHelper.HashString(people);
创建密码/字符串哈希是很常见的。所以我们拥有它。
/*constant: e6fb7af54c39f39507c28a86ad98a1fd*/
string name = "Dipon Roy";
string value = new HashHelper().HashString(name);
原文地址:https://www.codeproject.com/Tips/5130239/Csharp-Generating-Hash-String