2016-08-31 72 views
9

Güçlü bir şekilde yazılan iş nesnelerini anonim türlere eşleyen, daha sonra JSON'a serileştirilen ve bir API aracılığıyla gösterilen bazı kodlar var. Çözümümün ayrı projelere yeniden yapılandırılmasından sonra bazı testlerim başarısız olmaya başladı. Biraz kazma yaptım ve bu Object.Equals farklı bir derleme tarafından kod tarafından döndürülen anonim türlerde farklı davranıyor - ve neden emin değilim, ya da etrafında çalışmak için ne yapabilirim.Object.Equals(), farklı derlemeler tarafından oluşturulduğunda neden aynı anonim türler için yanlış döndürüyor?

https://github.com/dylanbeattie/AnonymousTypeEquality numaralı telefondan tam bir repro kodu var ama aslında kırılan biraz değer aşağıda. Bu kod Testler projede geçerli:

[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 }; 
    } 
} 

ardından içeren çözeltide ayrı sınıf kütüphanesi var ancak bu:

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

göre MSDN için, "aynı anonim türden iki örneği yalnızca tüm özellikleri eşit olduğunda eşittir. " (Benim vurgu) - Yani karşılaştırma amacıyla iki örnek aynı anonim tip olup olmadığını kontrol eder? İki örneğimin eşi karma kodları var ve her ikisi de <>f__AnonymousType0`2[System.String,System.Int32] olarak görünüyor - ancak anonim türlerin eşitliğinin tam olarak nitelenen tür adını hesaba katması gerektiğini ve bu nedenle kodun farklı bir gruba taşınmasını sağladığını tahmin ediyorum. Bunun nasıl uygulandığı konusunda kesin bir kaynak/bağlantı var mı?

cevap

4

bir aracı kullanarak meclisleri sökerseniz:

Burada anon türleriyle yapılacak benim fave şeylerden biri Reflektör gibi, size anonim tipi (derleyici tarafından oluşturulan tanımlayıcıları unmangling sonra) şöyle her derleme bir sınıfın ile temsil edilir göreceksiniz:

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() 
    { 
     // ... 
    } 
} 

value olup Equals yöntem kontrolleri ilk hat akımı düzeneği içinde tanımlı sınıfı özellikle atıfta bulunarak, AnonymousType<TName, TValue> bir örneğidir.Böylece, farklı meclislerden gelen anonim tipler, aynı yapıya sahip olsalar bile, asla eşitliği karşılaştırmazlar.

Testlerinizi, nesnelerin kendileri yerine, serileştirilmiş JSON ile karşılaştırmak için değiştirmek isteyebilirsiniz.

8

Anonim türler doğası gereği kapsamlıdır. Örneğiniz kapsam belirliyor, bu yüzden türler farklı. Geçerli C# derleyicilerinde, anonim türler derlemeleri (veya daha kesin olması için modülleri) aşamaz. İki farklı derlemeden iki anonim tür aynı özelliklere sahip olsa bile, bunlar iki farklı tiptedir (ve internal, dolayısıyla güvenlik etkilerinden sakınınız). object'a anonim bir yazı yazdığınızda, 'u yanlış anlıyorsunuz demektir.

TL; DR: Anonim türlerini kötüye kullanıyorsunuz. Şaşırmayın, seni ısırdı.

+0

Bir süre içinde ısırılmakta zorlanıyorum ... ısırığı provoke etmek için ne yaptığınızı tam olarak bilmek güzel. :) –

+1

@DylanBeattie 'object' için anonim bir yazı yazdınız. Bunu asla yapma. Jenerik türü (LINQ gibi) ya da yerel olarak doğru türde kaldığınızdan emin olun. Diğer her şey prohibidado. – Luaan

2

Anonim türler, tanımları eşleştiğinde etkinlik amacıyla yeniden kullanılan, içinde yaşadıkları aksamın içinde gizli bir Tür olarak derlenir. Bu, farklı meclislerdeki benzer AT'lerin farklı Tipler içereceği anlamına gelir ve onların .Equals Tip kontrolü yapar.

void Main() 
{ 
    var json = "{ \"name\": \"Dylan\"}"; 
    var x = Deserialize(json, new { name = null as string}); 
    Console.WriteLine(x.name); 
} 

T Deserialize<T>(string json, T template) 
{ 
    return (T) JsonConvert.DeserializeObject(json, typeof(T)); 
} 

Başka montaj Deserialize yöntemini koymak ilginç olurdu ...