Created
April 3, 2018 09:03
-
-
Save tocsoft/25c389ca97b74b241c27a1ecd3b66c92 to your computer and use it in GitHub Desktop.
Object Hasher
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.CSharp.RuntimeBinder; | |
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.ComponentModel; | |
using System.Dynamic; | |
using System.Linq; | |
using System.Runtime.CompilerServices; | |
using System.Text; | |
using System.Threading.Tasks; | |
namespace ObjectHash | |
{ | |
public static class ObjectExtentions | |
{ | |
static string nullHash = "".Hash(); | |
public static string Hash<T>(this T @object) | |
=> @object.Hash(c => c); | |
public static string Hash<T, Y>(this T @object, Func<T, Y> projection) | |
{ | |
Y target = default(Y); | |
if (@object != null) | |
{ | |
target = projection(@object); | |
} | |
if (IsSimple(typeof(Y))) | |
{ | |
return target?.ToString().Hash(); | |
} | |
if(target == null) | |
{ | |
return nullHash; | |
} | |
return target.ToDictionary().HashDictionary(); | |
} | |
private static string HashDictionary(this Dictionary<string, object> dictionary) | |
{ | |
var key = dictionary.GenerateKey(); | |
return Hash(key); | |
} | |
private static string Hash(this string str) | |
{ | |
var data = Encoding.Unicode.GetBytes(str); | |
var hasher = System.Security.Cryptography.SHA256.Create(); | |
var hashData = hasher.ComputeHash(data); | |
return Convert.ToBase64String(hashData); | |
} | |
private static string GenerateKey(this Dictionary<string, object> dictionary) | |
{ | |
StringBuilder sb = new StringBuilder(); | |
InnerGenerateKey(dictionary, sb); | |
return sb.ToString(); | |
} | |
private static void InnerGenerateKey(Dictionary<string, object> dictionary, StringBuilder sb) | |
{ | |
var orderdKeys = dictionary.Keys.OrderBy(x => x); | |
foreach (var k in orderdKeys) | |
{ | |
sb.Append('@'); | |
sb.Append(k.Replace("@", "@@")); | |
sb.Append('@'); | |
var val = dictionary[k]; | |
if (val != null) | |
{ | |
if (IsSimple(val.GetType())) | |
{ | |
sb.Append(val.ToString()); | |
} | |
else | |
{ | |
InnerGenerateKey(val.ToDictionary(), sb); | |
} | |
} | |
} | |
} | |
private static bool IsSimple(Type type) | |
{ | |
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) | |
{ | |
// nullable type, check if the nested type is simple. | |
return IsSimple(type.GetGenericArguments()[0]); | |
} | |
return type.IsPrimitive | |
|| type.IsEnum | |
|| type.Equals(typeof(string)) | |
|| type.Equals(typeof(decimal)) | |
|| type.Equals(typeof(DateTime)); | |
} | |
///<summary> | |
/// Convert an object into a dictionary | |
///</summary> | |
///<param name="object">The object</param> | |
///<param name="nullHandler">Handler for null value</param> | |
///<returns></returns> | |
public static Dictionary<string, object> ToDictionary(this object @object) | |
{ | |
if (@object == null) | |
{ | |
return new Dictionary<string, object>(); | |
} | |
var @objectAsDict = @object as IDictionary; | |
if (@objectAsDict != null) | |
{ | |
var hashFromDict = new Dictionary<string, object>(@objectAsDict.Count); | |
foreach (var key in @objectAsDict.Keys) | |
{ | |
hashFromDict.Add(key.ToString(), @objectAsDict[key]); | |
} | |
return hashFromDict; | |
} | |
var properties = TypeDescriptor.GetProperties(@object); | |
DynamicObject dyn = @object as DynamicObject; | |
int count = properties.Count; | |
List<string> dynNames = null; | |
if (dyn != null) | |
{ | |
dynNames = dyn.GetDynamicMemberNames().ToList(); | |
count += dynNames.Count; | |
} | |
var hash = new Dictionary<string, object>(count); | |
foreach (PropertyDescriptor descriptor in properties) | |
{ | |
var key = descriptor.Name; | |
var value = descriptor.GetValue(@object); | |
hash.Add(key, value); | |
} | |
if (dynNames != null) | |
{ | |
Type objType = @object.GetType(); | |
foreach (string dynName in dynNames) | |
{ | |
var key = dynName; | |
var value = GetValue(dynName, dyn, objType); | |
hash.Add(key, value); | |
} | |
} | |
return hash; | |
} | |
public static object GetValue(string name, DynamicObject dyn, Type objType) | |
{ | |
try | |
{ | |
var callSite = | |
CallSite<Func<CallSite, object, object>>.Create( | |
Microsoft.CSharp.RuntimeBinder.Binder.GetMember(CSharpBinderFlags.None, name, objType, | |
new[] | |
{ | |
CSharpArgumentInfo.Create( | |
CSharpArgumentInfoFlags.None, null) | |
})); | |
return callSite.Target(callSite, dyn); ; | |
} | |
catch (RuntimeBinderException) | |
{ | |
return null; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment