Il casting e la conversione di tipo sono modi diversi di trasformare un'espressione da un tipo di dato a un altro.
La documentazione di C# classifica la conversione di tipo come l'uso dell'operatore as o dell'operatore is. Il casting è definito come l'uso dell'operatore di cast.
In C#, molto spesso, al di fuori dell'ambito dei valori numerici e delle gerarchie di classi, dovrai fare una conversione chiamando un membro del tipo «to», come Int32.Parse(), che converte una stringa in un intero, oppure chiamando un membro del tipo «from», ad esempio object.ToString(). Gli sviluppatori che usano Javascript e altri linguaggi dinamici dovrebbero tenerlo presente.
Nota che sono disponibili gli operatori di cast impliciti ed espliciti (di cui parliamo in operator-overloading), che possono introdurre un casting abbastanza arbitrario nei tipi che definisci.
Il sistema dei tipi di C# è un po' più rigido di quello di C o di Javascript, e di conseguenza le operazioni di casting sono più limitate. Il casting implicito avviene tra due tipi numerici purché il tipo «to» possa preservare la scala e il segno del valore del tipo «from». Nota, nella documentazione, l'eccezione relativa alla conversione in numeri reali, dove la precisione può andare persa.
Un cast implicito non è indicato da alcuna sintassi speciale. Ad esempio:
int myInt = 1729;
long myLong = myInt;
Non esiste una conversione implicita di un'espressione numerica (o di una stringa) in bool. La Base Class Library (BCL) fornisce Convert.ToBoolean() a questo scopo.
Un'espressione di tipo char può essere convertita implicitamente in int. Il cast nella direzione opposta deve essere esplicito. Non tutti i valori di int sono caratteri utf 16 validi.
Quando i tipi numerici non possono essere convertiti implicitamente, in genere puoi usare l'operatore di cast esplicito.
Quando il valore sottoposto a cast non può essere rappresentato dal tipo «to» perché non è abbastanza ampio o c'è un conflitto di segno, allora nel caso degli interi può essere lanciata un'eccezione di overflow, mentre nel caso dei float e dei double la variabile del tipo «to» può assumere il valore Infinity.
Un'espressione di tipo int può essere convertita esplicitamente in char. Questo può produrre un char non valido.
int largeInt = Int32.MaxValue;
int largeNegInt = Int32.MinValue;
ushort shortUnsignedInt = ushort.MaxValue;
float largeFloat = float.MaxValue;
float smallFloat = 17.29f;
// implicit cast
int from_ushort = shortUnsignedInt; // 65535
float from_int = largeInt; // -21474836E+09
int from_char = 'a'; // 96
// explicit cast
uint from_largeInt = (uint)largeInt; // 2147483647
uint from_neg = (uint) largeNegInt; // 2147483648 or OverflowException is thrown (if checked)
int from_smallFloat = (int) smallFloat; // 17
int from_largeFloat = (int) largeFloat; // -2147483648 or OverflowException is thrown (if checked)
char from_intc = (char) 32; // ' '
char from_invalid_int = (char) 0xdcbf; // invalid char - no exception thrown
// no cast available
int fromString = Int32.Parse("42"); // 42
string toString = largeInt.ToString(); // "2147483647"
int fromString_bad = Int32.Parse("forty two"); // FormatException is thrown
Vedi questo articolo per la parola chiave checked.
Qualsiasi tipo può essere convertito implicitamente nella sua classe base o interfaccia.
IList<int> ll = new List<int>();
Un operatore di cast può essere usato per convertire da una classe base a una classe derivata.
IList<int> ll = new List<int>();
List<int> l = (List<int>)ll;
Se il cast fallisce perché il tipo dell'espressione che viene convertita non è correlato al tipo di destinazione, viene lanciata un'istanza di InvalidCastException.
Un approccio più comune a queste conversioni di tipo è usare la parola chiave is:
interface IFoo { int Bar(); }
class Foo : IFoo { public int Bar() { return 42; }}
Object r = new Random();
IFoo ifoo = new Foo();
Object o = new Foo();
int result = 1729;
if (ifoo is Foo foo)
{
result = foo.Bar();
}
// result == 42
result = 1729;
if (o is Foo foo2)
{
result = foo2.Bar();
}
// result == 42
result = 1729;
if (r is Foo foo3)
{
result = foo3.Bar();
}
// result == 1729
La parola chiave as svolge una funzione simile a is, ad esempio var foo = ifoo as Foo;. In questo esempio foo sarà null se ifoo non è di tipo Foo, altrimenti ifoo le verrà assegnato.
I tipi possono definire i propri operatori di cast espliciti e impliciti personalizzati.
Esempi di cast espliciti e impliciti nella BCL sono le conversioni dalla struct BigInteger da e verso altri tipi numerici
typeof
Se devi rilevare il tipo preciso di un oggetto, allora is potrebbe essere un po' troppo permissivo, dato che convertirà un oggetto in una classe o in una delle sue classi base. typeof e Object.GetType() sono la soluzione in questo caso.
object o = new List<int>();
o is ICollection<int> // true
o.GetType() == typeof(ICollection<int>) // false
o is List<int> // true
o.GetType() == typeof(List<int>) // true