1

I am trying to compare two arrays in C# as posted in previous SO post:

Why is the following not working for me:

var first = Value as Array;
var second = other.Value as Array;
bool equal = first.SequenceEqual(second);

I get:

CS1061: 'Array' does not contain a definition for 'SequenceEqual' and no accessible extension method 'Array' accepting a first argument of type 'Array' could be found (are you missing a using directive or an assembly reference?).

I do have the right using at the top:

using System.Linq;

since I can write (no compiler error):

var first = Value as string[];
var second = other.Value as string[];
bool equal = first.SequenceEqual(second);

For reference, I am trying to implement the Equals operator for a generic value type:

public struct MyValue<T> : IEquatable<MyValue<T>> 
{
  public T Value { get; set; }
  public bool Equals(VRValue<T> other) => throw new NotImplementedException();
}
malat
  • 12,152
  • 13
  • 89
  • 158
  • 1
    `Array` implements `IEnumerable`, but `SequenceEqual` is only defined for `IEnumerable` – SomeBody Sep 10 '21 at 12:14
  • 1
    Rather than checking for `Array`, a better way could be to check for [`IStructuralEquatable`](https://learn.microsoft.com/en-us/dotnet/api/system.collections.istructuralequatable?view=net-5.0), which `Array` (and a few other collections) implements. – Sweeper Sep 10 '21 at 12:25
  • You can use Linq with an `Array` by casting its elements, e.g. `bool equal = first.Cast().SequenceEqual(second.Cast());` – Matthew Watson Sep 10 '21 at 14:08

2 Answers2

2

SequenceEqual works with IEnumerable<T>, but Array implements only non-generic IEnumerable, so SequenceEqual does not applicable here.

When casting to the string[], you got different (typed) type of array, which exactly implements IEnumerable<T> (where T is string for this sample)

Serg
  • 3,454
  • 2
  • 13
  • 17
1

Using @Sweeper/@Jeppe Stig Nielsen suggestions, I rewrote my function as:

public bool Equals(VRValue<T> other)
{
    if (typeof(T).IsArray)
    {
        var first = Value as IStructuralEquatable;
        var second = other.Value as IStructuralEquatable;
        return StructuralComparisons.StructuralEqualityComparer.Equals(first, second);
    }
  [...]
}

When using a HashSet, one should also pay attention to GetHashCode:

public override int GetHashCode()
{
    if (typeof(T).IsArray)
    {
        var first = Value as IStructuralEquatable;
        return StructuralComparisons.StructuralEqualityComparer.GetHashCode(first);
    }
    [...]
}
malat
  • 12,152
  • 13
  • 89
  • 158
  • 1
    Consider `return StructuralComparisons.StructuralEqualityComparer.Equals(first, second);`. (What you have now, is just checking for sameness (i.e. reference equality), so it is equivalent to `return first == second;` which will say false if the two operands are distinct arrays with identical lengths and identical entries.) – Jeppe Stig Nielsen Sep 10 '21 at 13:06
  • 1
    For "get hash code" you must use the relevant method, not just some other one! So certainly `return StructuralComparisons.StructuralEqualityComparer.GetHashCode(first);` now that you follow my suggestion from the first comment. Hash codes should match the `Equals` used, or else all is nonsense. `EqualityComparer.Default` is really wrong in your case. – Jeppe Stig Nielsen Sep 10 '21 at 13:25