hash.cs

using sysCols = System.Collections;
using sysCon = System.Console;

class MainApp{
  public static void Main(){
    string[] cc = new string[] {"rossi", "verdi", "bianco", "nerone"};
    string[] nn = new string[] {"pino", "gino", "nino", "rino"};
    
    sysCols.Hashtable PhoneBook = new sysCols.Hashtable();
    for ( int i = cc.GetLowerBound(0); i <= cc.GetUpperBound(0); i++ )
      for ( int j = nn.GetLowerBound(0); j <= nn.GetUpperBound(0); j++ )
      {
        Person key = new Person(cc[i], nn[j]);
        PhoneNumber item = new PhoneNumber(0431 + i,  123450 + (10*i) + j);
        PhoneBook.Add(key, item);
      }
    
    Person rp  = new Person(cc[0], nn[0]);
    PhoneBook[rp] =  new PhoneNumber(113, 113);    

    int cnt = 0;
    sysCon.WriteLine("#\tNOMINATIVO\t\tNUMERO");
    foreach(sysCols.DictionaryEntry pb in PhoneBook)
    {
      Person p = (Person)pb.Key;
      PhoneNumber n = (PhoneNumber)pb.Value;
      sysCon.WriteLine("{0}\t{1} {2}\t\t{3}-{4}", ++cnt, p.Nome, p.Cognome, n.Prefisso, n.Numero);
    };


    return;
  }
}


class PhoneNumber{
  private int prefisso;
  private int numero;

  public PhoneNumber(int prefisso, int numero){
    this.prefisso = prefisso;
    this.numero = numero;
  }
  public int Prefisso{
    get{return prefisso;}
  }
  public int Numero{
    get{return numero;}
  }
}

class Person{
  private readonly string nome;
  private readonly string cognome;

  // Per abilitare gli oggetti di tipo Person ad essere utilizzati utilmente come elementi 
  // key di collezioni (per esempio System.Collections.Hashtable) è necessario fare
  // l'override del metodo GetHashCode() e di Equals per implementare il confronto per 
  // valore.
  // In caso contrario, il confronto di default è per riferimento e quindi istanze di 
  // elementi key di ugale valore risultano comunque diversi.
  //
  // 1 If two objects of the same type represent the same value, the hash function must 
  //   return the same constant value for either object. 
  // 2 A hash function should be based on an immutable data member. The hash function should
  //   return exactly the same value regardless of any changes that are made to the object. 
  //   Basing the hash function on a mutable data member can cause serious problems, including 
  //   never being able to access that object in a hash table
  // 3 Implementations of GetHashCode must not throw exceptions
  // 4 Derived classes that override GetHashCode must also override Equals to guarantee that
  //   two objects considered equal have the same hash code;
  // 5 For the best performance, a hash function should generate a random distribution 
  //   for all input. 
  //
  // (1) è verificata perchè se due oggetti hanno Nome e Cognome uguali, avranno anche lo 
  // stesso codice Hash; (2) è verificata perch Nome e Cognome vengono assegnati solo nel
  // costruttore e mai più modificati; (3)è  verificata perchè usa GetHashCode() di string
  // che a sua volta non solleva eccezioni; (4) è sicuramente verificata se non faccio 
  // l'override di Equals perchè Equals di default è per riferimento e quindi fa un 
  // confronto solo per identità;
  //

  
  // Provare a commentare GetHashCode e Equals e notare la difformità di comportamento
  public override int GetHashCode() {
    return (cognome + nome).GetHashCode();
  }
  
  public override bool Equals(object obj) { 
    if (obj == (object)this) return true;
    //Check for null and compare run-time types (quello della classe della istanza). 
    if (obj == null || GetType() != obj.GetType()) return false;
    Person p = (Person)obj;
    return nome == p.nome && cognome == p.cognome;
  }
  
  

  public Person(string nome, string cognome){
    this.nome = nome;
    this.cognome = cognome;
  }
  public string Nome{
    get {return nome;}
  }
  public string Cognome{
    get {return cognome;}
  }
}