为什么Object.Equals()在从不同的程序集中实例化时,对于相同的匿名类型返回false?

Dyl*_*tie 10 .net c# equality anonymous-types

我有一些代码将强类型业务对象映射到匿名类型,然后将其序列化为JSON并通过API公开.

在将我的解决方案重组为单独的项目后,我的一些测试开始失败.我已经做了一些挖掘,事实证明,Object.Equals对来自不同程序集的代码返回的匿名类型的行为有所不同 - 我不知道为什么,或者我可以做些什么来解决它.

https://github.com/dylanbeattie/AnonymousTypeEquality有完整的repro代码,但实际上破坏的位在下面.此代码位于测试项目中:

[TestFixture]
public class Tests {
    [Test]
    public void BothInline() {
        var a = new { name = "test", value = 123 };
        var b = new { name = "test", value = 123 };
        Assert.That(Object.Equals(a,b)); // passes
    }

    [Test]
    public void FromLocalMethod() {
        var a = new { name = "test", value = 123 };
        var b = MakeObject("test", 123);
        Assert.That(Object.Equals(a, b)); // passes
    }

    [Test]
    public void FromOtherNamespace() {
        var a = new { name = "test", value = 123 };
        var b = OtherNamespaceClass.MakeObject("test", 123);
        Assert.That(Object.Equals(a, b)); // passes
    }


    [Test]
    public void FromOtherClass() {
        var a = new { name = "test", value = 123 };
        var b = OtherClass.MakeObject("test", 123);

        /* This is the test that fails, and I cannot work out why */
        Assert.That(Object.Equals(a, b));
    }

    private object MakeObject(string name, int value) {
        return new { name, value };
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在解决方案中有一个单独的类库,只包含这个:

namespace OtherClasses {
  public static class OtherClass {
    public static object MakeObject(string name, int value) {
      return new { name, value };
    }
  }  
}
Run Code Online (Sandbox Code Playgroud)

根据MSDN," 只有当它们的所有属性相等时," 同一匿名类型的两个实例才相等." (我的重点) - 那么为了进行比较,是什么控制两个实例是否具有相同的匿名类型?我的两个实例具有相同的哈希码,两者似乎都是<>f__AnonymousType0`2[System.String,System.Int32]- 但我猜测匿名类型的相等性必须考虑完全限定的类型名称,因此将代码移动到不同的程序集可能会破坏事物.任何人都有一个确切的来源/链接确切如何实现?

Lua*_*aan 8

匿名类型本质上是作用域.您的示例打破了范围,因此类型不同.在当前的C#编译器中,匿名类型不能超越程序集(或模块,更准确).即使来自两个不同程序集的两个匿名类型具有相同的属性,它们也是两种不同的类型(它们是internal,因此要注意安全隐患).第二个你将一个匿名类型转发给object你,你知道你做错了.

TL; DR:你在滥用匿名类型.不要惊讶它咬你.


Mic*_*Liu 5

如果使用Reflector这样的工具反汇编程序集,您将看到您的匿名类型由每个程序集中的类表示,如下所示(在解组编译器生成的标识符之后):

internal sealed class AnonymousType<TName, TValue>
{
    private readonly TName _name;
    private readonly TValue _value;

    public TName name => this._name;
    public TValue value => this._value;

    public AnonymousType(TName name, TValue value)
    {
        this._name = name;
        this._value = value;
    }

    public override bool Equals(object value)
    {
        var that = value as AnonymousType<TName, TValue>;
        return that != null &&
            EqualityComparer<TName>.Default.Equals(this._name, that._name) &&
            EqualityComparer<TValue>.Default.Equals(this._value, that._value);
    }

    public override int GetHashCode()
    {
        // ...
    }
}
Run Code Online (Sandbox Code Playgroud)

方法的第一行Equals检查是否value是实例AnonymousType<TName, TValue>,特指当前程序集中定义的类.因此,即使它们具有相同的结构,来自不同程序集的匿名类型也永远不会相等.

您可能希望更改测试以比较对象的序列化JSON而不是对象本身.