gpt4 book ai didi

c# - T[].Contains for struct 和 class 表现不同

转载 作者:可可西里 更新时间:2023-11-01 08:28:39 25 4
gpt4 key购买 nike

这是一个后续问题: List<T>.Contains and T[].Contains behaving differently

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)

在这里,通用 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)

非通用 Equals 被调用,带走了实现 `IEquatable 的好处。

为什么数组调用Equals struct[] 不同和 class[] , 尽管这两个系列看起来都很普通

数组的怪异令人沮丧,我正在考虑完全避免它......

注意: Equals 的通用版本仅当结构 实现 IEquatable<T> 时调用. 如果类型没有实现 IEquatable<T> , Equals 的非泛型重载无论它是否是 class 都会被调用或 struct .

最佳答案

看起来实际上并不是 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)

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

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)

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

如果您在 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")]

我不熟悉 TypeDependencyAttribute,但从评论中,我想知道是否有一些特殊的魔法正在发生,这是数组的特殊之处。这可以解释 IndexOf[T] 是如何通过 Array 的 IList.Contains 最终被调用而不是 IndexOf 的。

关于c# - T[].Contains for struct 和 class 表现不同,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19888123/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com