0

まず、私はジェネリックに慣れていないので、間違いがあることをあらかじめお詫びします。

さまざまな種類の辞書を統一的に比較できるようにしたいと考えています。このタイプを持つ可能性のある 2 つの辞書を比較します (明確にするために、同じタイプを持つ 2 つの異なる辞書を比較したいと思います)。

Dictionary<string, Int32>
Dictionary<Int32, Int32>
Dictionary<string, string>
Dictionary<string, List<Int32>>
Dictionary<Int32, List<Int32>>
Dictionary<Int32, List<ComplexObject>>

ジェネリックを使用して、次のコードにたどり着きました。

 private static bool DictionaryEquals<TKey, TValue>(Dictionary<TKey, TValue> left, Dictionary<TKey, TValue> right)
    {
        var comp = EqualityComparer<TValue>.Default;
        if (left.Count != right.Count)
        {
            return false;
        }
        foreach (var pair in left)
        {
            TValue value;
            if ((typeof(TValue).Namespace == "System.Collections.Generic"))
            {
                TValue rightValue;
                right.TryGetValue(pair.Key, out rightValue);
                return ListEquals<TValue>(new List<TValue>(pair.Key), rightValue);
            }

            if (right.TryGetValue(pair.Key, out value) || (!comp.Equals(pair.Value, value)))
            {
                return false;
            }
        }
        return true;
    }

    private static bool ListEquals<TValue>(List<TValue> left, List<TValue> right)
    {
        if (left.Count != right.Count)
        {
            return false;
        }

        return left.All(right.Contains);
    }

ListEquals メソッドの呼び出しに問題があります。pair.key および rightValue パラメーターを渡す方法がわかりません。

ご提案ありがとうございます

4

4 に答える 4

0

このようなことをすべきではありませんか?

public class DictionaryEqualityComparer<TKey,TValue> : IEqualityComparer<Dictionary<TKey,TValue>>
{
    public bool Equals( Dictionary<TKey , TValue> x , Dictionary<TKey , TValue> y )
    {
        bool unequal =  x.Count != y.Count
                     || x.Except( y ).Any() // this is probably redundant
                     || y.Except( x ).Any() // but my caffiene titration is off this AM
                     ;
        return !unequal ; 
    }
    public int GetHashCode( Dictionary<TKey , TValue> obj )
    {
        return obj.GetHashCode() ;
    }

いくつかの理由で。最大のものは、同じタイプの2つの辞書がDictionary<string,Widget>、キーに異なる等価比較子を使用できることです。StringComparer文字列キーのインスタンスでは、ストック実装のいずれかである可能性があります。これは「平等」の概念を複雑にします。

于 2012-11-28T18:31:07.827 に答える
0

TValueが rightValue の型である場合、TValue はList<SomeType>です。ただし、ListEquals を使用するのではなく、List<SomeValue>

于 2012-11-28T17:35:56.457 に答える
0

私はあなたのコードを変更しましたが、これは機能していますが、 を置き換える方法がわかりませんdynamic。これは最良の選択ではありませんが、すべてのキャストの問題を解決します。

        private static bool DictionaryEquals<TKey, TValue>(Dictionary<TKey, TValue> left, Dictionary<TKey, TValue> right)
        {
            var comp = EqualityComparer<TValue>.Default;
            if (left.Count != right.Count)
            {
                return false;
            }

            if (typeof(TValue).IsGenericType && typeof(TValue).GetGenericTypeDefinition() == typeof(List<>))
            {
                return left.All(pair => right.ContainsKey(pair.Key) && ListEquals((dynamic)pair.Value, (dynamic)right[pair.Key]));            
            }
            else
            {
                return left.All(pair => right.ContainsKey(pair.Key) && comp.Equals(pair.Value, right[pair.Key]));
            }
        }

        private static bool ListEquals<TValue>(List<TValue> left, List<TValue> right)
        {
            if (left.Count != right.Count)
            {
                return false;
            }

            return left.All(right.Contains);
        }
于 2012-11-28T17:58:08.663 に答える
0

問題に対する私のアプローチは次のとおりです。

private static bool DictionaryEquals<TKey, TValue>(Dictionary<TKey, TValue> left, Dictionary<TKey, TValue> right)
{
    var comp = EqualityComparer<TValue>.Default;
    if (left.Count != right.Count)
    {
        return false;
    }

    if (left.Keys.Intersect(right.Keys).Count() != left.Count)
        return false;
        //there is a key in the left dictionary that's not in the right dictionary
        //if there are any keys in the right dictionary not in the left then either 
        //there is one in the left not in the right as well, or the counts won't have 
        //been equal, so we know the two key sets are equal.

    var defaultValueComparer = EqualityComparer<TValue>.Default;

    Func<TValue, TValue, bool> valueComparer;

    if (typeof(TValue) is IEnumerable)
        valueComparer = (first, second) => ((IList)first).SequenceEqual((IList)second);
    else
        valueComparer = (first, second) => defaultValueComparer.Equals(first, second);

    foreach (var key in left.Keys)
    {
        if (!valueComparer(left[key], right[key]))
            return false;
    }

    return true;
}

public static bool SequenceEqual(this IList first, IList second)
{
    if (first.Count != second.Count)
        return false;

    IEnumerator iterator1 = first.GetEnumerator(),
                iterator2 = second.GetEnumerator();

    while (true)
    {
        bool next1 = iterator1.MoveNext();
        bool next2 = iterator2.MoveNext();
        // Sequences aren't of same length. We don't 
        // care which way round. 
        if (next1 != next2)
        {
            return false;
        }
        // Both sequences have finished - done 
        if (!next1)
        {
            return true;
        }
        if (!object.Equals(iterator1.Current, iterator2.Current))
        {
            return false;
        }
    }
}

注意すべき重要な点がいくつかあります。

2 つのセットを「セット比較」にするのではなく、シーケンス比較にしました。それらが本当に一連の比較を持つ必要がある場合は、そもそもそれらがリスト以外のものである方が理にかなっています。とにかく、それが重要な変更である場合は、私のSequenceEqualメソッドの名前と実装を次のように変更できます。

public static bool SetEquals(this IList first, IList second)
{
    if (first.Count != second.Count)
        return false;

    return first.OfType<object>().Intersect(second.OfType<object>())
        .Count() < first.Count;
}

foreach の内部で値を比較する方法を決定するよりも、外部で比較する方が理にかなっています。値の型はループ内で変更されません。比較関数が何であるかを一度決定してから、それを何度も呼び出すだけです。デリゲートはこれに最適です。

于 2012-11-28T18:05:09.043 に答える