gpt4 book ai didi

c# - GetHashCode 和 Equals 在 System.Attribute 中实现不正确?

转载 作者:可可西里 更新时间:2023-11-01 07:58:16 24 4
gpt4 key购买 nike

Artech's blog看然后我们在评论中进行了讨论。由于那个博客只有中文,所以我在这里做一个简单的解释。重现代码:

[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public abstract class BaseAttribute : Attribute
{
public string Name { get; set; }
}

public class FooAttribute : BaseAttribute { }

[Foo(Name = "A")]
[Foo(Name = "B")]
[Foo(Name = "C")]
public class Bar { }

//Main method
var attributes = typeof(Bar).GetCustomAttributes(true).OfType<FooAttribute>().ToList<FooAttribute>();
var getC = attributes.First(item => item.Name == "C");
attributes.Remove(getC);
attributes.ForEach(a => Console.WriteLine(a.Name));

该代码获取所有的 FooAttribute 并删除名称为“C”的那个。明明输出的是“A”和“B”?如果一切顺利,你就不会看到这个问题。事实上,理论上你会得到“AC”“BC”甚至更正“AB”(我的机器上有 AC,博客作者有 BC)。问题是由 System.Attribute 中 GetHashCode/Equals 的实现引起的。实现的一个片段:

  [SecuritySafeCritical]
public override int GetHashCode()
{
Type type = base.GetType();
      //*****NOTICE*****
FieldInfo[] fields = type.GetFields(BindingFlags.NonPublic
| BindingFlags.Public
| BindingFlags.Instance);
      object obj2 = null;
for (int i = 0; i < fields.Length; i++)
{
object obj3 = ((RtFieldInfo) fields[i]).InternalGetValue(this, false, false);
if ((obj3 != null) && !obj3.GetType().IsArray)
{
obj2 = obj3;
}
if (obj2 != null)
{
break;
}
}
if (obj2 != null)
{
return obj2.GetHashCode();
}
return type.GetHashCode();
}

它使用 Type.GetFields 所以从基类继承的属性被忽略,因此 FooAttribute 的三个实例是等价的(然后是 Remove 方法随机)。那么问题来了:执行上有什么特殊原因吗?或者这只是一个错误?

最佳答案

一个明显的错误,不。一个好主意,也许是也可能不是。

一件事等于另一件事是什么意思?如果我们真的想的话,我们可以变得非常哲学。

只是有点哲学,有几件事必须坚持:

  1. 平等是自反的:身份意味着平等。 x.Equals(x)必须持有。
  2. 平等是对称的。如果x.Equals(y)然后y.Equals(x)如果!x.Equals(y)然后!y.Equals(x) .
  3. 平等是可传递的。如果x.Equals(y)y.Equals(z)然后x.Equals(z) .

还有一些其他的,虽然只有这些可以直接反射(reflect)在 Equals() 的代码中。一个人。

如果实现重写 object.Equals(object) , IEquatable<T>.Equals(T) , IEqualityComparer.Equals(object, object) , IEqualityComparer<T>.Equals(T, T) , ==!=不满足以上,就是明显的bug。

.NET 中反射(reflect)平等的其他方法是 object.GetHashCode() , IEqualityComparer.GetHashCode(object)IEqualityComparer<T>.GetHashCode(T) .这里有一个简单的规则:

如果a.Equals(b)那么它必须持有 a.GetHashCode() == b.GetHashCode() .等价于 IEqualityComparerIEqualityComparer<T> .

如果那不成立,那么我们又遇到了一个错误。

除此之外,没有关于平等必须意味着什么的全面规则。取决于自身提供的类的语义Equals()覆盖或由相等比较器强加给它的那些。当然,这些语义应该非常明显,或者记录在类或相等比较器中。

总而言之,Equals 是如何实现的?和/或 GetHashCode有一个错误:

  1. 如果它无法提供上面详述的自反、对称和传递属性。
  2. 如果GetHashCode之间的关系和 Equals和上面的不一样。
  3. 如果它与其记录的语义不匹配。
  4. 如果它抛出不适当的异常。
  5. 如果它进入无限循环。
  6. 在实践中,如果返回所需的时间太长以至于削弱了东西,尽管有人可能会争辩说这里存在理论与实践的区别。

覆盖了 Attribute , equals 确实具有自反、对称和传递属性,它是 GetHashCode确实匹配它,它的文档是 Equals覆盖是:

This API supports the .NET Framework infrastructure and is not intended to be used directly from your code.

你真的不能说你的例子反驳了这一点!

由于您提示的代码在这些方面都没有失败,所以它不是错误。

这段代码中有一个错误:

var attributes = typeof(Bar).GetCustomAttributes(true).OfType<FooAttribute>().ToList<FooAttribute>();
var getC = attributes.First(item => item.Name == "C");
attributes.Remove(getC);

你首先要求一个满足条件的项目,然后要求删除一个等于它的项目。没有理由不检查所讨论类型的相等性语义就期望 getC将被删除。

你应该做的是:

bool calledAlready;
attributes.RemoveAll(item => {
if(!calledAlready && item.Name == "C")
{
return calledAlready = true;
}
});

也就是说,我们使用一个谓词来匹配第一个属性为Name == "C"的谓词。没有别的。

关于c# - GetHashCode 和 Equals 在 System.Attribute 中实现不正确?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8839445/

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