Last active
January 21, 2020 19:02
-
-
Save neuecc/082a0152877c393e58735c7d66af3850 to your computer and use it in GitHub Desktop.
This file contains 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 System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Reflection; | |
using System.Text; | |
namespace Cysharp.Reflection | |
{ | |
public static class DiffUtil | |
{ | |
public static DifferentValue[] GetDiffResult<T>(T left, T right, bool useFullName = false) | |
{ | |
var result = new List<DifferentValue>(); | |
StructuralEqual(left, right, new[] { useFullName ? typeof(T).FullName : typeof(T).Name }, ref result); | |
return result.ToArray(); | |
} | |
public static string DumpDiffResult<T>(T left, T right, bool useFullName = false) | |
{ | |
var result = GetDiffResult(left, right, useFullName); | |
if (result.Length == 0) | |
{ | |
return "No diff found."; | |
} | |
else | |
{ | |
var sb = new StringBuilder(); | |
foreach (var item in result) | |
{ | |
sb.AppendLine($"{string.Join(".", item.Names)} Left:{item.Left?.ToString()} Right:{item.Right?.ToString()}"); | |
} | |
return sb.ToString(); | |
} | |
} | |
static object GetValue(MemberInfo memberInfo, object value) | |
{ | |
if (memberInfo is PropertyInfo pi) | |
{ | |
return pi.GetGetMethod(true).Invoke(value, null); | |
} | |
else if (memberInfo is FieldInfo fi) | |
{ | |
return fi.GetValue(value); | |
} | |
throw new ArgumentException(); | |
} | |
static void StructuralEqual(object left, object right, IEnumerable<string> names, ref List<DifferentValue> context) | |
{ | |
if (object.ReferenceEquals(left, right)) return; | |
if (left == null || right == null) | |
{ | |
context.Add(new DifferentValue { Left = left, Right = right, Names = names }); | |
return; | |
} | |
var lType = left.GetType(); | |
var rType = right.GetType(); | |
if (lType != rType) | |
{ | |
context.Add(new DifferentValue { Left = left, Right = right, Names = names }); | |
return; | |
} | |
var type = left.GetType(); | |
// not object(int, string, etc...) | |
if (Type.GetTypeCode(type) != TypeCode.Object) | |
{ | |
var same = left.Equals(right); | |
if (!same) | |
{ | |
context.Add(new DifferentValue { Left = left, Right = right, Names = names }); | |
} | |
return; | |
} | |
// is sequence | |
if (typeof(IEnumerable).IsAssignableFrom(type)) | |
{ | |
SequenceEqual((IEnumerable)left, (IEnumerable)right, names, ref context); | |
return; | |
} | |
// IEquatable<T> | |
var equatable = typeof(IEquatable<>).MakeGenericType(type); | |
if (equatable.IsAssignableFrom(type)) | |
{ | |
var result = (bool)equatable.GetMethod("Equals").Invoke(left, new[] { right }); | |
if (!result) | |
{ | |
context.Add(new DifferentValue { Left = left, Right = right, Names = names }); | |
} | |
return; | |
} | |
// is object | |
ObjectEqual(left, right, names, ref context); | |
} | |
static void ObjectEqual(object left, object right, IEnumerable<string> names, ref List<DifferentValue> context) | |
{ | |
var fields = left.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public); | |
var properties = left.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(x => x.GetGetMethod(false) != null); | |
var members = fields.Cast<MemberInfo>().Concat(properties); | |
foreach (var mi in fields.Cast<MemberInfo>().Concat(properties)) | |
{ | |
var concatNames = names.Concat(new[] { (string)mi.Name }); | |
object lv = GetValue(mi, left); | |
object rv = GetValue(mi, right); | |
StructuralEqual(lv, rv, concatNames, ref context); | |
} | |
} | |
static void SequenceEqual(IEnumerable leftEnumerable, IEnumerable rightEnumarable, IEnumerable<string> names, ref List<DifferentValue> context) | |
{ | |
var le = leftEnumerable.GetEnumerator(); | |
using (le as IDisposable) | |
{ | |
var re = rightEnumarable.GetEnumerator(); | |
using (re as IDisposable) | |
{ | |
var index = 0; | |
while (true) | |
{ | |
object lValue = null; | |
object rValue = null; | |
var lMove = le.MoveNext(); | |
var rMove = re.MoveNext(); | |
if (lMove) lValue = le.Current; | |
if (rMove) rValue = re.Current; | |
if (lMove && rMove) | |
{ | |
StructuralEqual(lValue, rValue, names.Concat(new[] { "[" + index + "]" }), ref context); | |
} | |
if ((lMove == true && rMove == false) || (lMove == false && rMove == true)) | |
{ | |
var diff = new DifferentValue { Left = lValue, Right = rValue, Names = names.Concat(new[] { "[" + index + "]" }) }; | |
context.Add(diff); | |
return; | |
} | |
if (lMove == false && rMove == false) break; | |
index++; | |
} | |
} | |
} | |
} | |
} | |
public class DifferentValue | |
{ | |
public object Left; | |
public object Right; | |
public IEnumerable<string> Names; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment