sort4.cs

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

class MainApp{
  public static void Main(){
    string[] cc = new string[] {"rossi", "Verdi", "bianco", "Nerone"};
    string[] nn = new string[] {"pino", "gino", "nino", "rino", "Pino", "GINO", "Gino"};

    // Dichiaro e popolo la collezione PhoneBook di tipo SortedList
    System.Collections.SortedList PhoneBook = new System.Collections.SortedList();
    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);
      }

    // Visito la collezione ordinata secondo il criterio espresso dagli elementi
    // di tipo Key (Person)
    sysCon.WriteLine("NOMINATIVO\tNUMERO TEL.");
    foreach(sysCols.DictionaryEntry pb in PhoneBook)
    {
      Person p = (Person)pb.Key;
      PhoneNumber n = (PhoneNumber)pb.Value;
      sysCon.WriteLine("{0} {1}\t{2}-{3}", p.Cognome, p.Nome, n.Prefisso.ToString("0###"), n.Numero);
    };

    // Confronto alcuni elementi 
    sysCon.WriteLine("\n\nPerson(\"Di Tacco\", \"Gino\").Equals(Person(\"Guatenalbare\", \"Petrocle\")): {0}",
                     (new Person("Di Tacco", "Gino")).Equals(new Person("Guatenalbare", "Petrocle")));    
    sysCon.WriteLine("Person(\"Di Tacco\", \"Gino\").Equals(Person(\"Di Tacco\", \"Gino\")): {0}",
                     (new Person("Di Tacco", "Gino")).Equals(new Person("Di Tacco", "Gino")));    
    sysCon.WriteLine("\nPerson(\"Di Tacco\", \"Gino\") == Person(\"Di Tacco\", \"Gino\"): {0}",
                     (new Person("Di Tacco", "Gino")) == (new Person("Di Tacco", "Gino")));    
    sysCon.WriteLine("Person(\"Di Tacco\", \"Gino\") != Person(\"Di Tacco\", \"Gino\"): {0}",
                     (new Person("Di Tacco", "Gino")) != (new Person("Di Tacco", "Gino")));    
    sysCon.WriteLine("\nPerson(\"Guatenalbare\", \"Petrocle\") == Person(\"Guatenalbare\", \"PETROCLE\"): {0}",
                     (new Person("Guatenalbare", "Petrocle")) == (new Person("Guatenalbare", "PETROCLE")));    

    // Visualizzo alcuni codici hash
    sysCon.WriteLine("\nnew Person(\"Guatenalbare\", \"Petrocle\").GetHashcode(): {0}",
                     (new Person("Guatenalbare", "Petrocle")).GetHashCode());    
    sysCon.WriteLine("new Person(\"Guatenalbare\", \"Petrocle\").GetHashcode(): {0}",
                     (new Person("Guatenalbare", "Petrocle")).GetHashCode());    

    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: System.IComparable {
  private readonly string nome;
  private readonly string cognome;

  public Person(string cognome, string nome){
    this.cognome = cognome;
    this.nome = nome;
  }

  public string Cognome{
    get {return cognome;}
  }

  public string Nome{
    get {return nome;}
  }

  // Per abilitare gli oggetti di tipo Person ad essere utilizzati come elementi key
  // di collezioni con ordinamento (per esempio System.Collections.SortedList) è necessario
  // implementare l'interfaccia IComparable.
  // 
  // IComparable.CompareTo(object obj) Return Value Meaning 
  // a) Less than zero This instance is less than obj. 
  // b) Zero This instance is equal to obj. 
  // c) Greater than zero This instance is greater than obj. 
  //
  // d) By definition, any object compares greater than a null reference; and two null 
  //    references compare equal to each other.
  // e) The parameter, obj, must be the same type as the class or value type that implements
  //    this interface; otherwise, an ArgumentException is thrown.
  //
  // For any objects A, B and C, the following must be true:
  // f) A.CompareTo(A) is required to return zero.
  // g) If A.CompareTo(B) returns zero then B.CompareTo(A) is required to return zero.
  // h) If A.CompareTo(B) returns zero and B.CompareTo(C) returns zero then 
  //   A.CompareTo(C) is required to return zero.
  // i)If A.CompareTo(B) returns a value other than zero then B.CompareTo(A) is required to 
  //   return a value of the opposite sign.
  // j) If A.CompareTo(B) returns a value x not equal to zero, and B.CompareTo(C) returns 
  //   a value y of the same sign as x, then A.CompareTo(C) is required to return a value 
  //   of the same sign as x and y.
  int System.IComparable.CompareTo(object obj)
  {
    if (obj == (object)this) return 0; // (f)
    if (this.GetType() != obj.GetType()) throw new System.ArgumentException(); // (e)
    if (obj == null) return +1; // (d)

    sysDia.Debug.Assert(GetType() == obj.GetType() && obj != null);
    string a = this.cognome + "-" + this.nome;
    Person p = (Person)obj;
    string b = p.cognome + "-" + p.nome;
    return a.CompareTo(b); // (a) (b) (c) (g) (h) (i) (j)
  }

  // Per l'Ordinamento viene utilizzata l'interfaccia IComparable mentre per testare
  // l'ugualianza (in generale, non dalla collezione) viene utilizzato il metodo Equals. 
  // IComparable e Equals devono operare coerentemente. Quindi la classe che implementa
  // IComparable deve fare l'override di Equals.
  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 || this.GetType() != obj.GetType()) return false;
    Person p = (Person)obj;
    return this.cognome == p.cognome && this.nome == p.nome;
  }

  // La classe che fa l'override di Equals deve fare l'override di GetHashCode coerentemente.
  // 
  // 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. 
  public override int GetHashCode() {
    return (cognome + "-" + nome).GetHashCode();
  }


  // La classe che fa l'override di Equals implementando una semantica di confronto per
  // valore deve fare l'overload dell'operatore di ugualianza == coerentemente.
  public static bool operator ==(Person p, Person q) {
    object op = (object)p;
    object oq = (object)q;

    if (op == oq) return true;
    if ((op == null) || (oq == null)) return false;
    return q.cognome == p.cognome && q.nome == p.nome;
  }

  // E di conseguenza quello di disuguaglianza !=
  public static bool operator !=(Person p, Person q) {
   return !(p == q); 
  }

  
}