Jump to content
  • C# (C Sharp) Lezione #2 - Programmazione Guida passo per passo


     Share

    var

    Solo per dichiarare variabili locali, il tipo (inferito) è quello dell'inizializzatore

     

    Esempio:

    int i=0;
    var i=0; //equivalenti, finchè non cambia inizializz.

     

    E' chiaramente molto comoda con i generici...
     

    List<List<string>> lls = new List<List<string>>();
    var lls=new List<List<string>>();

    Non solo per i pigri, essenziale in alcuni casi

     

    Proprietà (Properties)

    Metodi get/set che invocati attraverso la sintassi di accesso a campo, per esempio,

    exp.P = 3
    • usate per caching, lazy loading, ecc..
    • è possibile specificare solo il getter o il setter
    • Per il setter, value indica il valore "scritto"

     

    Al client "sembrano" accessi a campo: è ragionevole usarle solo se getter/setter efficienti

    In quanto metodi, possono essere virtual, abstract, comparire nelle interfacce

    Uno dei due può avere un modificatore di accesso più restrittivo (per es. il setter private)

     

    Uso ovvio con getter e setter

    public class C {
    	private int_i;
    	
      	public int I {
          get {
            return this._i;
          }
          set { 
            this._i=value;
          }
        }
    }

     

    Vediamo come richiamare la classe e le sue proprietà;

    C aC = new C();
    aC.I = 41;
    ++aC.I // !!!
      Console.WriteLine(aC.I);
    	//stampa 42

     

    Auto-Properties

     

    Come una property, ma il campo nascosto è generato automaticamente dal compilatore

     - non se ne conosce il nome

    publi class C {
      public int I { get; set; }
      public int K { get; }
      public int Z { get; private set; }
    }

     

    Con ReSharper la trasformazione fra una versione e l'altra richiede solo un ALT Return...

    Le Auto-Properties si possono inizializzare:

     - sinstassi analoga ai campi

    public int I { get; set; } = 37;

     - come per i campi

    . inizializzazioni eseguite nell'ordine in cui sono scritte.

    . nell'espressione di inizializzazione non si può fare riferimento al this.

     

    Properties senza campo - Esempio:

    public class Person {
      public int YearOfBirth { get; set; }
      public int Age {
        get {
          return DateTime.Now.Year - this.YearOfBirth;
        }
      }
    }
    // ...
    var p=new Person { YearOfBirth=1973};
    Console.WriteLine(p.Age);

     

    Scorciatoie varie

    Maniera compatta per dichiarare un Array se conosciamo i valori, sparito il tipo string in var, sparita la dimensione che lo deduce da solo.

    var array = new[] {"ciao", "mondo" };

     

    Costruire un oggetto ed assegnare ai campi qualche valore

    var anA = new A(3) { G=3 };
    var anotherA = new A { F=5, G=3 };

     

    Qui costruisco una nuova lista ma subito inserisco 3 elementi

    var ls = new List<string>() {"qui", "quo", "qua" };

     

    Ora vediamo il dizionario con chiave e valore

    var d = new Dictionary <int, string> () { {1, "pippo"}, {2, "pluto"} };
      // oppure:
    var d1 = new Dictionary<int, string> () { {[1]="pippo"}, {[2]="pluto" } };

     

    Operator overloading

    In molti linguaggi alcuni operatori sono overloaded (per es. quelli aritmetici)

    C# permette di definire il comportamento di molti operatori, quando (almeno) un operando è di un tipo definito dall'utente

     - Non si possono aggiungere operatori o variarne la precedenza

     - Non si può ridefinire l'assegnazione (nemmeno struct)

    Gli operatori vanno definiti public static e hanno come nome operator op

     - Per chi conosce C++: "this" non è l'operando sinistro, negli operatori binari servono due parametri

     

    Esempio di uso dell'overloading

    DateTime x = DateTime.Now;
    DateTime y = new DateTime(1982,1,1);
    TimeSpan t = x - y;
    DateTime k = x + t;
    
    DateTime TimeSpan sono due tipi (struct) di sistema

     

    Operator overloading esempio:

    public class C {
      public static C operator +(C x, int y) {
        return null;
      }
    }
    
    public class Program {
      public static void Main(string[] args) {
        var c = new C();
        C foo = c+3;
        c+=27; // come scrivere c=c+27;
      }
    }

     

    • Alcuni operatori (== e != e <,>= e <=) vanno a coppie, se si definisce uno , si deve definire anche l'altro
    • Se si definisce == è buona norma ridefinire anche Equals e GetHashCode

     

    class C {
      private int x, y;
      public override bool Equals(Object obj) {
        if (obj == null)
          return false;
        if (obj.GetType()==typeof(C)) {
          C other = (C) obj;
          return this.x == other.x && this.y == other.y;
        }
        return false;
      }
      public override int GetHashCode() {
        return...
        }
    }

     

    User-defined conversions

    Il tipo sorgente o destinazione deve corrispondere al tipo che contiene la conversione

    public class C {
      public static implicit operator C(int i) {
        return null;
      }
      public static implicit operator int(C from) {
        return 1;
      }
      public static explicit operator string(C from) {
        return "a";
      }
    }
    	// ...
    C c=42;
    int i=c;
    string s=(string)c;

     

    Indexer

    Misto fra proprietà e overloading di [] (una classe può dichiarare diversi indexer, in overload)

    var h=new Dictionary<string, int>();
    h["ciao"]=7;
    public class C {
      private int _x
        public int this[double a, double b] {
        get {return 42;}
        set {this._x=value;}
      }
    }
    //...
    var c=new C();
    c[4,2.0]=7;
    int i = c[Math.PI,0];
    // Si possono usare per inizializzare
      var conversion = new Dictionary<string, int> {
      ["diciotto"]=18,
      ["diciannove"]=19,
      ...
        

     

    Dynamic

    In C# 4 è arrivato il tipo dynamic che corrisponde al tipo <<staticamente dinamico>> (=un tipo che richiede binding dinamico)

     - Molto elegante, ma dietro le quinte diventa Object e viene usata la reflection a palate (=lento, come usare un linguaggio dinamicamente tipato)

      . è un uso furbo/ottimizzato della reflection, si vedano gli expression tree e il DLR

     - Comodo per certi scopi, per es. interfacciarsi a linguaggi dinamicamente tipati

    Dynamic è totalmente diverso da var

     

    Delegate

    Tipi assimilabili ai tipi funzionali di Caml

      - un delegate permette di invocare uno o più metodi conoscendone solo tipo di ritorno e tipo dei parametri

    La dichiarazione di un tipo delegate D, per es.

    public delegate int D(int a, int b); // int x int -> int

    definisce una classe che estende System.Delegate

       - essendo classsi, l'equivalenza di tipi delegate è per nome: public delegate int Foo(int a, int b); definisce un tipo delegate diverso (incompatibile) da D anche se entrambi descrivono un metodo che, presi due interi, restituisce un intero.

     

    Se D è un tipo delegate, un oggetto di tipo D si ottiene tramite new, passando come argomento il nome di un metodo; per esempio: new D(nomeMetodo);

      - Nota: nomeMetodo potrebbe individuare più di un metodo per via dell'overloading, in quel caso viene scelto il più specifico

    Se pippo è un oggetto di tipo delegate, per invocare i metodi associati a pippo si usa la sintassi classica dell'invocazione: pippo(...);

     

    public class Program {
    	public delegate int D(int a, int b);
    	public delegate int Foo(int a, int b);
      
    	private static int Sum(int x, int y) { return x + y; }
    	private int Max(int i, int j) { return Math.Max(i, j); }
      
    public static void Main(string[] args) {
    	D d1 = new D(Program.Sum);
    	Foo f1 = new Foo(Sum); // Sum = Program.Sum
    	Console.WriteLine(d1(1, 2));
    	Console.WriteLine(f1(1, 2));
    	D d2 = new D(new Program().Max);
    	Foo f2 = new Foo(new Program().Max);
    	Console.WriteLine(d2(1, 2));
    	Console.WriteLine(f2(1, 2));
    	// D d3 = f1; // errore!
    } }

     

    Grazie alle conversioni implicite..

    public class Program {
    	public delegate int D(int a, int b);
    	public delegate int Foo(int a, int b);
      
    	private static int Sum(int x, int y) { return x + y; }
    	private int Max(int i, int j) { return Math.Max(i, j); }
      
    public static void Main(string[] args) {
    	D d1 = Program.Sum;
    	Foo f1 = Sum; // Sum = Program.Sum
    	Console.WriteLine(d1(1, 2));
    	Console.WriteLine(f1(1, 2));
    	D d2 = new Program().Max;
    	Foo f2 = new Program().Max;
    	Console.WriteLine(d2(1, 2));
    	Console.WriteLine(f2(1, 2));
    	// D d3 = f1; // errore!
    } }

     

    Invocation-List

     

    Ogni (oggetto) delegate ha una invocation-list associata

       - Quando creiamo un delegate, l'unico elemento della lista è il metodo che abbiamo passato alla new

    Invocando un (oggetto) delegate, invochiamo in sequenza i metodi della sua invocation-list

     

    Operazioni sull'invocation-List

    Ogni tipo delegate D implicitamente definisce

    D operator +(D x, D y)

    D operator -(D x, D y)

    bool operator==(System.Delegate x, System.Delegate y)

    bool operator!=(System.Delegate x, System.Delegate y)

    Quindi i delegate possono essere

     - combinati con + e += (il delegate risultante corrisponde all'invocazione di tutti i metodi di x, seguiti da quelli di y)

     - rimossi con - e -=

     - confrontati per vedere se puntano la stessa lista di metodi

     

    Delegate generici

    Visto che sono classi, anche i delegate possono essere generici. Per esempio delegate int Comparer<T>(T x, T y);

    In .NET 3.x sono stati introdotti:

     - Azioni, metodi void che si aspettano T1...Tn:

       Action, Action<T>, Action<T1,T2>,...

     - Funzioni da T1...Tn in TResult:

       Func<TResult>, Func<T,TResult>, Func<T1,T2,TResult>,...

     - Predicati: Predicate<T>

     

    private static int Sum(int x, int y) { ... }
    private static int Max(int i, int j) { ... }
    
    public static void Main(string[] args) {
    	Func<int, int, int> f = Sum;
    	Func<int, int, int> g = Max;
    	Console.WriteLine( (f+g)(1, 2) ); // 2
    	var k = g + f;
    	Console.WriteLine( k(1, 2) ); // 3
    	Console.ReadLine();
    }

     

    Metodi anonimi

     

    Permettono di creare dei delegate a partire da un frammento di codice, esempio:

    Func<int, int, int> f = delegate(int a, int b) { return a+b};

    Non possiamo usare var per dichiarare f

    Sono chiusure (closure): possono accedere a variabili locali del metodo che li contiene

     

    Lambda Expressions (Closures)

    Assimilabili a lambda-astrazioni del lambda-calcolo, ovvero definizioni di funzioni "inline"

    Esempi:

    (int x, int y) => x + y; //somma x e y
    a = a * a; //funzione quadrato
    (x,y) => {if (x<y) return x; else return y;} //restituisce il minimo

     

    Il tipo dei parametri può essere inferito, se il parametro è solo uno non servono le parentesi

    Le lambda NON sono delegate, ma sono convertibili in delegate

     

    public static void Foo(Action<int> bar) {
    	bar(21);
    }
    public static void Main(string[] args) {
    	int k = 0;
    	Action<int> f = delegate(int a) { k += a; };
    	Action<int> g = a => k += a;
    	Foo(f);
    	Foo(g);
    	Console.WriteLine(k); // 42
    	Console.ReadLine();
    }

     

    Lambda-like syntax

    Sintassi molto simile alle lambda per

     - dichiarazione di proprietà

    public int Z {get {return I+K;}}
    public int Z => I+K;

     - dichiarazione di metodi

    public int Pippo(int x) {return Z+x;}
    public int Pippo(int x) => Z+x;

     

    EVENTI: Pattern Observer

    AKA: Dependents, Publish-Subscribe, Listener

    Motivazione: partizionando un sistema in una collezione di oggetti che cooperano nasce la necessità di mantenere la consistenza

    Ma senza ridurre la riusabilità delle classi

    Intento: definire una relazione 1 a molti fra oggetti, in modo che quando uno cambia stato, tutti quelli dipendenti vengano notificati

     

    Eventi in .NET

    Spesso è utile associare delle azioni all'accadere di un certo evento

    Per esempio il click su un pulsante, la pressione di un tasto, la ricezione di un messaggio dalla rete, ecc..

    Gli eventi in .NET portano a livello di linguaggio il pattern Observer

     - Permettono di disaccoppiare la sorgente dell'evento dalle azioni di risposta

     - Solo la classe che espone l'evento può sollevarlo, gli altri possono solo registrarsi per ricevere le notifiche

     

    Dichiarazione e uso

    Una classe può esporre degli eventi, dichiarati con: event TipoDelegate NomeEvento;

    Chi è interessato a un evento può usare::

     - l'operatore += per iscrivere un delegate, chiamato in questo caso, event-handler

     - l'operatore -= per de-iscrivere un event-handler

    L'vento può essere sollevato (fired) solo all'interno della stessa classe con: nomeEvento(...); tutti gli event-handler iscritti vengono invocati

     

    Per sollevare un evento, dobbiamo controllare che non sia null

    if (this.nomeEvento!=null)
      this.nomeEvento(/*...*/);

    non è thread-safe

    Possibilità 1:

    var ev=Volatile.Read(ref this.nomeEvento);
    if (ev!=null)
      ev(/*...*/);

    Possibilità 2:

    this.nomeEvento?(/*...*/);

    non si può.

    Si usa

    this.nomeEvento?.Invoke(/*...*/);

     

    Per certi versi, gli eventi stanno ai delegate come le proprietà stanno ai campi

    public event TipoDelegate NomeEvento;

    equivale ad avere un campo privato di tipo TipoDelegate e chiamato NomeEvento e due metodi pubblici add e remove

     

    delegate void EventHandler (object sender, BlablaEventArgs e);

    sender indica la sorgente dell'evento (es. button)

    e contiene le informazioni aggiuntive e BlablaEventArgs è un sottotipo di EventArgs (es. KeyPressEventArgs)

     

    public delegate void EventHandler<TEventArgs>(
      	Object sender, 
      	TEventArgs e)
      
    //Per esempio
    public class Stock {
      //...
      public event EventHandler<PriceChangedEventArgs> PriceChanged;
    }

     

     

     Share


    User Feedback

    Recommended Comments

    There are no comments to display.


×
×
  • Create New...

Important Information

Terms of Use Privacy Policy Guidelines We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.