本文共 5980 字,大约阅读时间需要 19 分钟。
目录
散列是将值转换为通常较短的固定长度的键/值,其表示原始值。几天前,我们不得不使用哈希比较来通过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);
原文地址:
转载地址:http://epzhj.baihongyu.com/