(Vedi Re[studing] C# 5 - (WinRT))
  • const versus static readonly

    Un campo dichiaranto come costante (const) è valutato staticamente in fase di compilazione e sostituto nel codice. Per questa ragione vanno dichiarati constanti i valori o le stringhe realmente immutabili nel tempo, pena side-effects non voluti nel caso di aggiornamento di un Assembly dipendente senza ricompilare, in questi casi definire il campo static readonly, .

  • Partial methods (C# 3.0)

    Utili nell'utilizzo di classi generate automaticamente quando si ha la necessità di modularizzare parti di implementazione da parte dell'utente, infatti la classe è compilabile anche quando il metodo non viene implementato, in questo caso la chiamata del metodo non viene generata. Il metodo deve essere privato e non deve avere tipo di ritorno (void).

    Code Snippet
    1.         partial class PartialExample
    2.         {
    3.             public PartialExample()
    4.             {
    5.                 CustomInitObject(); //If not implemented the call is not generated
    6.             }
    7.  
    8.             partial void CustomInitObject();
    9.         }
    10.  
    11.         partial class PartialExample
    12.         {
    13.             partial void CustomInitObject()
    14.             {
    15.                 //Implementation ...
    16.             }
    17.         }

  • class and inheritance

    • class A : B: A è la sottoclasse (o subclass), B è la superclasse (o superclass)
    • La conversione di una referenza di tipo A in una di tipo B è definita upcast ed è implicita
    • La conversione di una referenza di tipo B in una di tipo A è definita downcast e deve essere esplicita

  • structs

    • Le struct sono tipi-valore, per la precisione sono di classe System.ValueType, e di conseguenza implicitamente object
    • Le struct non supportano i parameterless constructor, finalizers e virtual members

    Code Snippet
    1.         struct S
    2.         {
    3.         }
    4.     
    5.         [Test]
    6.         public void TestStruct()
    7.         {
    8.             S s = default(S);
    9.  
    10.             Assert.IsInstanceOf<object>(s);
    11.             Assert.IsInstanceOf<ValueType>(s);
    12.         }

  • internal access modifier

    • Il modificatore internal rende visibili i tipi solo all'interno dello stesso assembly
    • I tipi internal possono anche essere visibili a friend assemblies utilizzando l'attributo InternalsVisibleToAttribute

  • flags enum

    • Quando un tipo enum è dichiarato con l'attributo [Flags], i suoi valori possono essere combinati con gli operatori bit a bit (come | e &)
    • La chiamata a ToString() su di un enum di tipo Flags produce l'elenco del nome dei valori separati da "," quando c'è corrispondenza altrimenti solo un numero

    Code Snippet
    1.         [Flags]
    2.         enum Options
    3.         {
    4.             One = 1,
    5.             Two = 2,
    6.             Three = 4
    7.         }
    8.  
    9.         [Test]
    10.         public void TestEnumFlags()
    11.         {
    12.             var o = Options.One | Options.Three;
    13.             Assert.AreEqual("One, Three", o.ToString());
    14.             var o2 = (Options) 6;
    15.             Assert.AreEqual("Two, Three", o2.ToString());
    16.             var o3 = (Options) 8;
    17.             Assert.AreEqual("8", o3.ToString());
    18.         }

  • Generics: Naked Type constraint (vedi C# Naked Type constraint)

    Un argomento di un tipo generic può essere dichiarato con la constraint where TY:TX indicando che TY deve essere di tipo TX.

    Code Snippet
    1.         class Generic1<TClass, TSubClass> where TSubClass : TClass
    2.         {
    3.             public bool IsTClassAssegnabileFromTSubClass {get { return typeof (TClass).IsAssignableFrom(typeof (TSubClass)); }}
    4.         }
    5.  
    6.         class A { }
    7.         class B:A { }
    8.  
    9.         [Test]
    10.         public void TestGenericUseWithNakedType()
    11.         {
    12.             var generic1 = new Generic1<object, A>();
    13.             var generic2 = new Generic1<A, B>();
    14.             //var generic3 = new Generic1<B, A>(); Compile error
    15.  
    16.             Assert.IsTrue(generic1.IsTClassAssegnabileFromTSubClass);
    17.             Assert.IsTrue(generic2.IsTClassAssegnabileFromTSubClass);
    18.         }

  • Generics: Self referencing declarations

    Un argomento di un tipo generic può essere dichiarato dello stesso tipo della classe, obbligando di fatto ad effettuare un subclassing per poterlo istanziare.

    Code Snippet
    1.         class SelfGeneric<T> where T : SelfGeneric<T> { }
    2.         class SubSelfGeneric : SelfGeneric<SubSelfGeneric> {}
    3.  
    4.         [Test]
    5.         public void TestGenericUseWithSelfType()
    6.         {
    7.             var generic1 = new SelfGeneric<SubSelfGeneric>();
    8.             var generic2 = new SubSelfGeneric();
    9.             //var generic3 = new SelfGeneric<object>(); //Compile error
    10.         }

  • Generics: Open/Close Type e differenze con i template C++

    • Un generic nella forma class<T> è definito un open type (che appunto è un modello di classe)
    • Quando dichiarato con il parametro, ad esempio class<int> è definito un close type (tipo concreto ed instanziabile a runtime)
    • In c++ il close type viene generato a compile time per questo le librerie di template c++ (ad es. ATL,STL) sono rilasciate come sorgente

  • Covariance and controvariance

    • Nel caso di A : B (A è implicitamente convertibile in B - vedi upcast) allora I<A> è implicitamente convertibile in I<B>, quest'ultima caratteristica è detta covarianza (covariance)
      • La covarianza è applicabile per le interfacce generic, i delegati generic, gli array, ma NON per le classi generic, questo nel rispetto della static type safety c#
      • Nelle interfacce generic la covarianza è supportata solo per i parametri modificati con out, esempio IProducer<out T> { T Produce(); }
        • Il modificatore out indica che T può essere indicato solo come parametro di output nel contratto dell'interfaccia
    • Quando I<B> è implicitamente convertibile in I<A> allora si parla di controvarianza (controvariance)
      • Questa caratteristica è possibile solo definendo un interfaccia controvariante, dove uno o più paremtri sono indicati con il modificatore in.
      • In questo caso il parametro decorato con il modificatore in può comparire solo come input nel contratto dell'intefaccia, esempio IConsumer<in T> { void Consume(T value); }

    NOTA: Stiamo parlando di interfacce cioè di contratti e non di tipi concreti, in questo caso la covarianza e la controvaianza sono meccanismi avanzati che ci permettono di dichiarare contratti che ci garantiscono il controlo sui tipi a compile tyme (static type safety). Riprendendo gil esempi sopra riportati (produttore, consumatore), nel caso della covarianza, stiamo dichiarando che un produttore ha un metodo che ritorna l'istanza di un oggetto di tipo T: dato un tipo concreto, ad esempio A (sottoclasse di B) possiamo sempre convertire implicitamente questo tipo nella sua superclasse (in B o in object), ecco perché IProducer<A> sarà convertibile in IProducer<B> ma NON vieceversa. In realtà quando parliamo di controvarianza stiamo facendo fondamentalmente la stessa operazione ma in modo simmetrico, infatti stiamo dichiarando che un consumatore ha un metodo che prende in input un oggetto di tipo T: dato un tipo concreto, ad esempio B (classe base) possiamo sempre convertire A (sottoclasse) in B ed è per questo che IConsumer<B> è convertibile in IConsumer<A> ma NON viceversa.