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に子フィールドのリストが含まれています。したがって、構造は階層的です。また、フィールドには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
}

私が達成したいのは、そのような複雑な構造の違いを取得することです。たとえば、ある種のComparerがあれば、フォーム全体の構造化された違いが1つのクラスとして得られるとしましょう。私が構造的な違いと言ったのは、そのようなものであるべきだという意味です。

  1. フィールドの違いForm(出力Formはバージョン フィールドに違いがあります (色が異なります)
  2. Fieldsフィールドから編集済みフィールドまでの階層を含む、コレクションの相違点
  3. FieldTypeとの同じ動作DataType
  4. への の削除と追加を検出FieldしますForm(したがって、おそらく各差分にはタイプがあります)

私が今持っているもの。私は一般的なアプローチから始め、IEqualityComparer インターフェイスを実装する ReflectionComparer を使用しようとしました

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;
}

しかし、おそらくそれは私が行きたい方法ではありません。なぜなら、各フィールドにはIDがあり、フォームごとに異なる可能性があることを考慮して、フィールドをより正確に比較する必要があり、比較プロセスをより制御したいからです。私にとっては、差分ツールのように聞こえます。

だから私は、違いを簡単に視覚化できるクライアントコードに公開するための良いパターンとかなり良い構造を探していますか?

4

3 に答える 3

1

あなたのクラスはすでに非常によく知られているので、私は次のものを使用します:

  • クラスにDiffWith(T other)クラスを持たせ、差異の列挙を返すIDiffableインターフェース。その場合、各クラスにDiffWithを実装することは理にかなっています。これは、ほとんど修正されているため、簡単なことです。

  • リフレクションを使用して、あなたが行う方法で比較する、別個のReflectionDiff(おそらく静的)クラス。これは、属性を使用して、プロパティ/フィールドをホワイトリストに登録/無視することができます。このようにして、差分をすばやく実装するためのデフォルトのdiffエンジンを使用し、使用しているリフレクションコードの種類を正確に使用できます。あなたは単に次のようなdiffの実装を持つことができます:

    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. グラフ比較ツールを作成し、2つのグラフを簡単に比較します

後でコードを公開して回答を更新します。

于 2013-01-10T17:38:49.587 に答える
0

あなたが求めているソリューションがどれほどエレガントかはわかりませんが、最小限のコーディングで非常に迅速なソリューションが必要な場合は、おそらく次のようになります。

  1. インスタンスをシリアル化して、テキスト形式 (JSON.NET を使用した JSON など) と比較します。
  2. シリアル化されたオブジェクトをテキスト ファイルに書き込む
  3. 2 つのテキスト ファイルを引数として渡す diff ツールを起動します (ExamDiff などで簡単に実行できます)。

私が言ったように、これは簡単な解決策ですが、あなたのニーズには十分かもしれません. それ以外の場合は、独自のビジュアライザーを作成できる UNIX スタイルの diff ファイルを出力するコマンド ライン diff ツールが利用可能です。

于 2012-12-21T10:18:52.543 に答える