2012-01-18 24 views
1

私は、監査ログのデータ構造の "深い比較"を行っています(誰がいつメンバーを変更したか)。私はこのためにリフレクションを使用しており、構造を再帰して比較しています。私は辞書を含む人たちには問題がありました。"Deep compare"辞書

会員が辞書であることをtypeof(IDictionary).IsAssignableFrom(memberType)で検出できます。私の計画は、両方のオブジェクトに存在するキーを収集し、それらのキーで再帰を続けます。ただし、IDictionary.KeysICollectionであり、LINQで拡張されていません。どのように私はこれを達成することができますキーの種類を知らない?

おそらく、この方法は、次善策です(私はジェネリックス/リフレクションコンボで経験はありません)。別の方法でこれを行うべきでしょうか?

+0

あなたの質問はどのようにLINQに関係していますか? – Seb

+0

@DavidM: 'IEnumerable'ではなく、テンプレートのバージョンのみです。私の質問の 'memberType'は' Dictionary 'となります。それは 'IDictionary'に割り当てられますが、私が知る限り、' IDictionary 'を実行することはできません。非総称の「IDictionary」で置き換えます。 – carlpett

答えて

0

自分で解決策を見つけました。ここではChangedPropertiesはプロパティ名を含む型であり、変更前/変更後の値です。

if (typeof(IDictionary).IsAssignableFrom(propertyType)) 
{ 
    Type keyType = propertyType.GetGenericArguments()[0], 
     valueType = propertyType.GetGenericArguments()[1]; 
    Type hashsetType = typeof(HashSet<>).MakeGenericType(keyType); 
    var hashsetCtor = hashsetType.GetConstructor(new[] { typeof(IEnumerable<>).MakeGenericType(keyType) }); 

    dynamic aDict = a; 
    dynamic bDict = b; 
    dynamic aKeys = hashsetCtor.Invoke(new object[] { aDict.Keys }); 
    dynamic bKeys = hashsetCtor.Invoke(new object[] { bDict.Keys }); 

    List<ChangedProperty> changes = new List<ChangedProperty>(); 
    foreach (var key in Enumerable.Intersect(aKeys, bKeys)) 
    { 
      // Present in both dictionaries. Recurse further 
    } 
    foreach (var key in Enumerable.Except(aKeys, bKeys)) 
    { 
      // Key was removed 
    } 
    foreach (var key in Enumerable.Except(bKeys, aKeys)) 
    { 
      // Key was added 
    } 

    return changes; 
} 
1

これは、反射の繰り返しに役立ちます。

IDictionary<int, string> t; 

bool t.GetType().IsGenericType 
Type[] t.GetType().GetGenericArguments() 
// you can do foreach here and see again if type is generic 

タイプがジェネリックであるかどうか最初にテストし、ジェネリック引数タイプを確認するヘルパーメソッドを作成できます。これにより、汎用辞書だけでなく、汎用引数を持つタイプもテストされます。 IList、KeyValuePairなど

public static bool IsType(Type inputType, Type targetType) 
{ 
    if (inputType.IsGenericType) 
    { 
     Type[] genericArgs = inputType.GetGenericArguments(); 
     var foundType = false; 
     foreach (var item in genericArgs) 
     { 
      if (IsType(item, targetType)) 
       foundType = true; 
     } 
     return foundType; 
    } 
    return inputType.IsAssignableFrom(targetType); 
}