8

假设我有这样的结构

public class Form
{
    #region Public Properties

    public List<Field> Fields { get; set; }

    public string Id { get; set; }

    public string Name { get; set; }

    public string Version { get; set; }

    public int Revision { get; set; }

    #endregion
}

所以Form类包含字段列表,假设字段本身是用这种结构表示的

public class Field
{
    #region Public Properties

    public string DisplayName { get; set; }

    public List<Field> Fields { get; set; }

    public string Id { get; set; }

    public FieldKind Type { get; set; }

    public FieldType FieldType { get; set; }

    #endregion
}

Field基本上是复合结构,每个都包含Field子字段列表。所以结构是分层的。Field 也引用了FieldType该类。

public class FieldType
{
    #region Public Properties

    public DataType DataType { get; set; }

    public string DisplayName { get; set; }

    public string Id { get; set; }

    #endregion
}

最后我们参考了DataType

public class DataType
{
    #region Public Properties

    public string BaseType { get; set; }

    public string Id { get; set; }

    public string Name { get; set; }

    public List<Restriction> Restrictions { get; set; }

    #endregion
}

我想要实现的是获得这种复杂结构的差异,比方说,如果我有某种比较器,它将给我作为一个类的整个表单的结构化差异,比如说差异结果。当我说结构化差异时,我的意思是它应该是这样的。

  1. 字段差异Form(输出Form在版本字段中有差异(不同颜色)
  2. 集合的差异Fields,包括字段与已编辑字段的层次结构
  3. FieldType和的行为相同DataType
  4. 检测删除和添加FieldForm(所以可能每个差异都会有一个类型)

我现在拥有的。我从通用方法开始,并尝试使用 ReflectionComparer 实现 IEqualityComparer 接口

public bool Equals(T x, T y)
{
    var type = typeof(T);

    if (typeof(IEquatable<T>).IsAssignableFrom(type))
    {
        return EqualityComparer<T>.Default.Equals(x, y);
    }

    var enumerableType = type.GetInterface(typeof(IEnumerable<>).FullName);

    if (enumerableType != null)
    {
        var elementType = enumerableType.GetGenericArguments()[0];
        var elementComparerType = typeof(DifferenceComparer<>).MakeGenericType(elementType);

        var elementComparer = Activator.CreateInstance(elementComparerType, new object[] { _foundDifferenceCallback, _existedDifference });

        return (bool)typeof(Enumerable).GetGenericMethod("SequenceEqual", new[] { typeof(IEnumerable<>), typeof(IEnumerable<>), typeof(IEqualityComparer<>) }).MakeGenericMethod(elementType).Invoke(null, new[] { x, y, elementComparer });
    }

    foreach (var propertyInfo in type.GetProperties())
    {
        var leftValue = propertyInfo.GetValue(x);
        var rightValue = propertyInfo.GetValue(y);

        if (leftValue != null)
        {
            var propertyComparerType = typeof(DifferenceComparer<>).MakeGenericType(propertyInfo.PropertyType);

            var propertyComparer = Activator.CreateInstance(propertyComparerType, new object[] {_foundDifferenceCallback, _existedDifference});

            if (!((bool)typeof(IEqualityComparer<>).MakeGenericType(propertyInfo.PropertyType)
                .GetMethod("Equals")
                .Invoke(propertyComparer, new object[] { leftValue, rightValue })))
            {
                // Create and publish the difference with its Type
            }
        }
        else
        {
            if (!Equals(leftValue, rightValue))
            {
                // Create and publish the difference with its Type
            }
        }
    }

    //return true if no differences are found
}

Difference

public struct Difference
{
    public readonly string Property;

    public readonly DifferenceType Type;

    public readonly IExtractionable Expected;

    public readonly IExtractionable Actual;
}

但可能这不是我想要的方式,因为我应该更精确地比较 Field,考虑到每个 Field 都有 Id,对于不同的形式也可能不同,我想更好地控制比较过程。对我来说,这听起来更像是一个差异工具。

所以我正在寻找好的模式和相当好的结构来将差异发布到客户端代码,这可以很容易地可视化它?

4

3 回答 3

1

鉴于您的课程已经众所周知,我将使用以下内容:

  • 一个 IDiffable 接口,使一个类具有一个 DiffWith(T other) 类,返回一个差异枚举。那么在你的每个类中实现你的 DiffWith 是有意义的,考虑到它们几乎是固定的,这应该是微不足道的。

  • 一个单独的 ReflectionDiff (可能是静态的)类,它将按照您的方式使用反射进行比较。这个可以使用属性来白名单/忽略任何属性/字段。这样,您可以拥有一个默认的差异引擎来快速实现差异,并准确使用您正在使用的反射代码。您可以简单地使用 diff 实现 like :

    public override IEnumerable<Difference> DiffWith(Field other)
    {
      return ReflectionDiff.Diff(this, other);
    }
    

您将获得一个包含所有脏反射代码的单个类(实际上并不需要),并且可以在每个 diffable 类中实现 DiffWith 的简单实现。

于 2012-12-20T09:46:27.433 回答
1

在调查了一下并比较了不同的资源后,我实现了这样的算法。

  1. 获取您的对象并构建图形(使用 .GetType().GetProperties(),将每个属性添加为根节点的子节点。节点的键将是属性名称。
  2. 如果遇到任何自定义类型或 IEnumerable 的属性,请递归执行。
  3. 创建图形比较器并简单比较两个图形

稍后我将发布我的代码并更新答案。

于 2013-01-10T17:38:49.587 回答
0

不确定您所追求的解决方案有多优雅,但如果您只是想要一个非常快速且编码最少的解决方案,那么也许:

  1. 序列化您的实例以与文本格式进行比较(例如使用 JSON.NET 的 JSON)
  2. 将序列化对象写入文本文件
  3. 启动一个 diff 工具,将 2 个文本文件作为参数传递(类似 ExamDiff 的东西很容易做到这一点)

就像我说的,这是一个快速的解决方案,但可能足以满足您的需求。否则,有可用的命令行 diff 工具将输出一个 unix 风格的 diff 文件,然后您可以为其编写自己的可视化工具。

于 2012-12-21T10:18:52.543 回答