T [].包含struct和class的行为方式不同

naw*_*fal 12 c# arrays generics struct equals

这是一个后续问题: List <T> .Contains和T [].包含的行为不同

T[].ContainsT类和结构时表现不同.假设我有这个结构:

public struct Animal : IEquatable<Animal>
{
    public string Name { get; set; }

    public bool Equals(Animal other) //<- he is the man
    {
        return Name == other.Name;
    }
    public override bool Equals(object obj)
    {
        return Equals((Animal)obj);
    }
    public override int GetHashCode()
    {
        return Name == null ? 0 : Name.GetHashCode();
    }
}

var animals = new[] { new Animal { Name = "Fred" } };

animals.Contains(new Animal { Name = "Fred" }); // calls Equals(Animal)
Run Code Online (Sandbox Code Playgroud)

在这里,正如我预期的那样正确调用泛型Equals.

但是在班级的情况下:

public class Animal : IEquatable<Animal>
{
    public string Name { get; set; }

    public bool Equals(Animal other)
    {
        return Name == other.Name;
    }
    public override bool Equals(object obj) //<- he is the man
    {
        return Equals((Animal)obj);
    }
    public override int GetHashCode()
    {
        return Name == null ? 0 : Name.GetHashCode();
    }
}

var animals = new[] { new Animal { Name = "Fred" } };

animals.Contains(new Animal { Name = "Fred" }); // calls Equals(object)
Run Code Online (Sandbox Code Playgroud)

非一般Equals叫做,带走执行`IEquatable的利益.

为什么阵列调用Equals不同的struct[]class[],即使两个集合似乎看通用

数组怪异是如此令人沮丧,我想完全避免它...

注意:Equals只有在struct 实现IEquatable<T>时才会调用泛型版本.如果该类型没有实现IEquatable<T>,非一般的过载Equals是不管它是所谓的class还是struct.

hat*_*ica 4

看来最终调用的实际上并不是 Array.IndexOf() 。查看源代码,如果是这种情况,我希望在这两种情况下都会调用 Equals(object) 。通过查看调用 Equals 时的堆栈跟踪,可以更清楚地了解为什么您会得到所看到的行为(值类型获取 Equals(Animal),但引用类型获取 Equals(object))。

这是值类型(struct Animal)的堆栈跟踪

at Animal.Equals(Animal other)
at System.Collections.Generic.GenericEqualityComparer`1.IndexOf(T[] array, T value, Int32 startIndex, Int32 count)
at System.Array.IndexOf[T](T[] array, T value, Int32 startIndex, Int32 count)
at System.Array.IndexOf[T](T[] array, T value)
at System.SZArrayHelper.Contains[T](T value)
at System.Linq.Enumerable.Contains[TSource](IEnumerable`1 source, TSource value) 
Run Code Online (Sandbox Code Playgroud)

这是引用类型(动物对象)的堆栈跟踪

at Animal.Equals(Object obj)
at System.Collections.Generic.ObjectEqualityComparer`1.IndexOf(T[] array, T value, Int32 startIndex, Int32 count)
at System.Array.IndexOf[T](T[] array, T value, Int32 startIndex, Int32 count)
at System.Array.IndexOf[T](T[] array, T value)
at System.SZArrayHelper.Contains[T](T value)
at System.Linq.Enumerable.Contains[TSource](IEnumerable`1 source, TSource value)
Run Code Online (Sandbox Code Playgroud)

由此您可以看到,调用的不是 Array.IndexOf,而是 Array.IndexOf[T]。该方法最终确实使用了相等比较器。对于引用类型,它使用调用 Equals(object) 的 ObjectEqualityComparer。对于值类型,它使用 GenericEqualityComparer 来调用 Equals(Animal),大概是为了避免昂贵的装箱。

如果您在http://www.dotnetframework.org上查看 IEnumerable 的源代码, 它的顶部有一个有趣的部分:

// Note that T[] : IList<t>, and we want to ensure that if you use
// IList<yourvaluetype>, we ensure a YourValueType[] can be used
// without jitting.  Hence the TypeDependencyAttribute on SZArrayHelper.
// This is a special hack internally though - see VM\compile.cpp.
// The same attribute is on IList<t> and ICollection<t>.
[TypeDependencyAttribute("System.SZArrayHelper")]
Run Code Online (Sandbox Code Playgroud)

我不熟悉 TypeDependencyAttribute,但从评论中,我想知道是否有一些对于 Array 来说是特殊的魔法。这可以解释如何最终通过 Array 的 IList.Contains 调用 IndexOf[T] 而不是 IndexOf。