2015-05-29 10 views
5

Ho scritto una classe di confronto personalizzata.Perché due nuovi oggetti non hanno lo stesso codice hash?

public class ItemComparer : IEqualityComparer<Item> 
{ 
    public int GetHashCode(Item x) 
    { 
     return (x == null) ? 0 : new { x.Name, x.CompanyCode, 
      x.ShipToDate, x.Address }.GetHashCode(); 
    } 

Ho un test che fallisce quando ho nuove fino due prodotti e confrontare i codici hash. Perché gli hash sono diversi?

[TestMethod] 
public void Two_New_Items_Have_The_Same_Hash_Code() 
{ 
    // arrange 
    var comparer = new ItemComparer(); 
    Item x = new Item(); 
    Item y = new Item(); 

    // act 
    int xHash = comparer.GetHashCode(x); 
    int yHash = comparer.GetHashCode(y); 

    // assert 
    Assert.AreEqual(xHash, yHash); 
} 

MODIFICA - Ecco la classe completa. Ho originariamente usato l'esempio di cui sopra per brevità, ma sono necessarie maggiori informazioni

public class DtoPolicy : DtoBase 
{ 
    [Description("The Policy Number")] 
    public string PolicyNumber { get; set; } 
    [Description("The Agent Code for the Agent who wrote the policy.")] 
    public string AgentCode { get; set; } 
    [Description("The First Name of the insured")] 
    public string FirstName { get; set; } 
    [Description("The Last Name of the insured")] 
    public string LastName { get; set; } 
    [Description("The Date of Birth of the insured")] 
    public DateTime DateOfBirth { get; set; } 
    [Description("The Age of the insured")] 
    public int Age { get; set; } 
    [Description("The Issue Date of the Policy")] 
    public DateTime PolicyIssueDate { get; set; } 
    [Description("The current status of the policy")] 
    public string PolicyStatus { get; set; } 
    public string TypeOfCoverage { get; set; } 
    public string PlanDescription { get; set; } 
    public decimal CompanyCode { get; set; } 
    public DateTime? TerminationDate { get; set; } 
    public decimal PolicyHolderSSN { get; set; } 
    [Description("The Zip Code of the insured")] 
    public string ZipCode { get; set; } 
    public decimal OwnerSSN { get; set; } 
    public string EmailAddress { get; set; } 
    public string WebUsername { get; set; } 
    public string OwnerFirstName { get; set; } 
    public string OwnerLastName { get; set; } 
    public string PayorFirstName { get; set; } 
    public string PayorLastName { get; set; } 
    public DateTime? PolicyEffectiveDate { get; set; } 
    public string AgentName { get; set; } 
    public string AgentPhone { get; set; } 
    public string InsuredCityState { get; set; } 
    public string InsuredAddress1 { get; set; } 
    public string InsuredAddress2 { get; set; } 
    public string InsuredCity { get; set; } 
    public string InsuredState { get; set; } 
    public string InsuredPhone { get; set; } 
    public string OwnerAddress1 { get; set; } 
    public string OwnerAddress2 { get; set; } 
    public string OwnerCity { get; set; } 
    public string OwnerState { get; set; } 
    public string OwnerZip { get; set; } 
    public string OwnerPhone { get; set; } 
    public string PayorAddress1 { get; set; } 
    public string PayorAddress2 { get; set; } 
    public string PayorCity { get; set; } 
    public string PayorState { get; set; } 
    public string PayorZip { get; set; } 
    public string PayorPhone { get; set; } 
    public DateTime? PaidToDate { get; set; } 
    public DateTime? LastPaidDate { get; set; } 
    public string PremiumMode { get; set; } 
    public decimal PremiumAmount { get; set; } 
    public DateTime? LastBillDate { get; set; } 
    public string BillingStatus { get; set; } 
    public decimal TotalLoanAmount { get; set; } 
    public decimal DividendAccumulation { get; set; } 
    public decimal ModalPremiumMonthly { get; set; } 
    public decimal ModalPremiumSemiAnnual { get; set; } 
    public decimal ModalPremiumQuarterly { get; set; } 
    public decimal ModalPremiumAnnual { get; set; } 
    public bool ElectronicBilling { get; set; } 
    public List<DtoClaim> Claims { get; set; } 
    public decimal MarketCode { get; set; } 
    public string BillingMode { get; set; } 

    public DtoPolicy() 
    { 
     Claims = new List<DtoClaim>(); 
    } 
} 

Questa implementazione di GetHashCode restituisce anche diversi hash per due nuovi oggetti

public int GetHashCode(DtoPolicy x) 
{ 
    return (x == null) ? 0 : x.Age.GetHashCode()^x.AgentCode.GetHashCode()^x.AgentName.GetHashCode()^x.AgentPhone.GetHashCode()^
     x.BillingMode.GetHashCode()^x.BillingStatus.GetHashCode()^
     x.Claims.GetHashCode()^x.CompanyCode.GetHashCode()^x.DateOfBirth.GetHashCode()^x.DividendAccumulation.GetHashCode()^
     x.ElectronicBilling.GetHashCode()^x.EmailAddress.GetHashCode()^x.FirstName.GetHashCode()^x.InsuredAddress1.GetHashCode()^
     x.InsuredAddress2.GetHashCode()^x.InsuredCity.GetHashCode()^x.InsuredCityState.GetHashCode()^x.InsuredPhone.GetHashCode()^
     x.InsuredState.GetHashCode()^x.LastBillDate.GetHashCode()^x.LastName.GetHashCode()^
     x.LastPaidDate.GetHashCode()^x.MarketCode.GetHashCode()^x.ModalPremiumAnnual.GetHashCode()^x.ModalPremiumMonthly.GetHashCode()^
     x.ModalPremiumQuarterly.GetHashCode()^x.ModalPremiumSemiAnnual.GetHashCode()^x.OwnerAddress1.GetHashCode()^
     x.OwnerAddress2.GetHashCode()^x.OwnerCity.GetHashCode()^x.OwnerFirstName.GetHashCode()^
     x.OwnerLastName.GetHashCode()^x.OwnerPhone.GetHashCode()^x.OwnerSSN.GetHashCode()^x.OwnerState.GetHashCode()^
     x.OwnerZip.GetHashCode()^x.PaidToDate.GetHashCode()^x.PayorAddress1.GetHashCode()^
     x.PayorAddress2.GetHashCode()^x.PayorCity.GetHashCode()^x.PayorFirstName.GetHashCode()^x.PayorLastName.GetHashCode()^
     x.PayorPhone.GetHashCode()^x.PayorState.GetHashCode()^x.PayorZip.GetHashCode()^
     x.PlanDescription.GetHashCode()^x.PolicyEffectiveDate.GetHashCode()^x.PolicyHolderSSN.GetHashCode()^
     x.PolicyIssueDate.GetHashCode()^x.PolicyNumber.GetHashCode()^x.PolicyStatus.GetHashCode()^
     x.PremiumAmount.GetHashCode()^x.PremiumMode.GetHashCode()^x.TerminationDate.GetHashCode()^
     x.TotalLoanAmount.GetHashCode()^x.TypeOfCoverage.GetHashCode()^x.WebUsername.GetHashCode()^x.ZipCode.GetHashCode(); 
} 
+3

È questo il tuo codice attuale? Non stai usando il comparatore da nessuna parte, e questo verrà compilato solo se c'è una conversione implicita da 'Item' a' int'. – Lee

+1

Questo non sembra giusto: 'int xHash = new Item();'. Penso che tu intenda 'int xHash = comparer.GetHashCode (x);'. – Guffa

+0

Scusa, ho corretto la parte 'act', l'ho scritta male. –

risposta

6

Presumo che si dispone di tipi complessi in uno dei tuoi progetti. Pertanto: Quando si ha un tipo complesso, GetHashCode è dirffent per ogni oggetto instanciated.

Potrebbe essere necessario implementare un metodo GetHashCode più dettagliato.,

Presumo che si desidera unire i codici hash di ogni proprietà per esempio come questo (non testato)

x.Name.GetHashCode()^x.CompanyCode.GetHashCode()^x.ShipToDate.GetHashCode()^x.Address.Id 

Questo post sul how to implement GetHashCode for structure descrive l'implementazione di hashcode per più proprietà (in questo caso con un struct).

+5

Se leggi [questo] (https://msdn.microsoft.com/it/it us/library/bb397696.aspx) dice "Poiché i metodi Equals e GetHashCode su tipi anonimi sono definiti in termini dei metodi Equals e GetHashCode delle proprietà, due istanze dello stesso tipo anonimo sono uguali solo se tutte le loro proprietà sono uguali ". – juharr

+2

Questo ovviamente non funzionerà se 'Address' è una classe che non sovrascrive' GetHashCode'. – juharr

+0

Ups soirry ha dimenticato di dire che presumo che l'indirizzo sia un tipo di compex. Devo anche aggiornare il mio exmaple alla destinazione indirizzo –

3

Se io definisco la classe Item come

public class Item 
{ 
    public string Name { get; set; } 
    public string CompanyCode { get; set; } 
    public DateTime ShipToDate { get; set; } 
    public List<string> Address { get; set; } 

    public Item() 
    { 
     //Uncomment Address initialization and the test fails.. 
     //Address = new List<string>(); 
    } 
} 

Poi il test passa per me. Quali sono i tipi di queste proprietà e come vengono inizializzate nel costruttore senza parametri?

Modifica: dopo l'aggiornamento, è presumibilmente lo new List<DtoClaim>() creato nel costruttore DtoPolicy.

+0

Ho abbreviato la classe perché ha circa 60 proprietà in esso, che i tipi possono essere 'stringa',' decimale', 'Elenco ', 'DateTime',' DateTime? ' e 'bool'. –

+2

@JonKittell Quindi il tuo problema è con 'Lista ' come 'GetHashCode' per' Lista 'non è sovrascritto e probabilmente otterrai un hash diverso per gli elenchi anche se hanno riferimenti identici all'interno di essi. – juharr

+0

@juharr: non è assolutamente vero. Il test passerà se i riferimenti dell'elenco sono nulli o se i riferimenti dell'elenco sono identici (puntare allo stesso elenco). – Rob

Problemi correlati