0

Inspired by

Comparing two collections for equality irrespective of the order of items in them

I created an extension method to test whether two collections are equivalent. To use the extension method, I have to specify two type parameters like this:

IsEquivalentToTestHelper<ObservableCollection<string>, string>(first, second, true);

Is there a way to implement the extension method so that only one generic constraint (e.g. ObservableCollection<string>) need be specified?

UPDATE: Posted the modified code based on answers as an additional response to the question that inspired this one.

Here's my original code:

static public class EnumerableExtensions 
{
    static public bool IsEquivalentTo<E,T>(this E first, E second) where E : IEnumerable<T>
    {
        if ((first == null) != (second == null))
            return false;

        if (!object.ReferenceEquals(first, second) && (first != null))
        {
            if (first.Count() != second.Count())
                return false;

            if ((first.Count() != 0) && HaveMismatchedElement<E,T>(first, second))
                return false;
        }

        return true;
    }

    private static bool HaveMismatchedElement<E,T>(E first, E second) where E : IEnumerable<T>
    {
        int firstCount;
        int secondCount;

        var firstElementCounts = GetElementCounts<E,T>(first, out firstCount);
        var secondElementCounts = GetElementCounts<E,T>(second, out secondCount);

        if (firstCount != secondCount)
            return true;

        foreach (var kvp in firstElementCounts)
        {
            firstCount = kvp.Value;
            secondElementCounts.TryGetValue(kvp.Key, out secondCount);

            if (firstCount != secondCount)
                return true;
        }

        return false;
    }

    private static Dictionary<T, int> GetElementCounts<E,T>(E enumerable, out int nullCount) where E : IEnumerable<T>
    {
        var dictionary = new Dictionary<T, int>();
        nullCount = 0;

        foreach (T element in enumerable)
        {
            if (element == null)
            {
                nullCount++;
            }
            else
            {
                int num;
                dictionary.TryGetValue(element, out num);
                num++;
                dictionary[element] = num;
            }
        }

        return dictionary;
    }

    static private int GetHashCode<E,T>(IEnumerable<T> enumerable) where E : IEnumerable<T>
    {
        int hash = 17;

        foreach (T val in enumerable.OrderBy(x => x))
            hash = hash * 23 + val.GetHashCode();

        return hash;
    }
}
Community
  • 1
  • 1
Eric J.
  • 147,927
  • 63
  • 340
  • 553
  • You're not really talking about generic *constraints* here - your code already has a single constraint. I suspect you're talking about only using one *type argument*. Would you be happy with extension methods targeting `Observable` specifically? (Also note that you're not even calling it as an extension method in the call you gave, and you seem to have three arguments instead of two...) – Jon Skeet Mar 11 '12 at 20:39
  • Jon: You're right, I only want one type argument. I got caught up thinking about how to specify the generic constraints and didn't realize I don't actually need one. – Eric J. Mar 11 '12 at 20:59

3 Answers3

4
static public bool IsEquivalentTo<T>(this IEnumerable<T> first, IEnumerable<T> second) 
the_joric
  • 11,986
  • 6
  • 36
  • 57
2

You could drop the first one and keep the second:

static public bool IsEquivalentTo<T>(this IEnumerable<T> first, IEnumerable<T> second)
zmbq
  • 38,013
  • 14
  • 101
  • 171
1

You just need to replace every E with IEnumerable<T>, and drop the where statements

For example:

static public bool IsEquivalentTo<T>(this IEnumerable<T> first, IEnumerable<T> second)

var firstElementCounts = GetElementCounts<IEnumerable<T>,T>(first, out firstCount);

static private int GetHashCode<T>(IEnumerable<T> enumerable)
Osguima3
  • 151
  • 10