Cast e conversioni di tipi (Guida per programmatori C#)

Dal momento che C# viene tipizzato in modo statico in fase di compilazione, una variabile già dichiarata non può essere dichiarata di nuovo o assegnata a un valore di un altro tipo, a meno che tale tipo non sia convertibile in modo implicito nel tipo della variabile. Ad esempio, string non può essere convertito in modo implicito in int. Pertanto, dopo aver dichiarato i come int, non è possibile assegnargli la stringa "Hello", come illustrato nel codice seguente:

int i;

// error CS0029: can't implicitly convert type 'string' to 'int'
i = "Hello";

Potrebbe tuttavia essere necessario copiare un valore in un variabile o un parametro di metodo di un altro tipo. Ad esempio, si potrebbe avere una variabile intera da passare a un metodo il cui parametro è tipizzato come double. O potrebbe essere necessario assegnare una variabile di classe a una variabile di un tipo interfaccia. Questi tipi di operazioni sono detti conversioni di tipi. In C#, è possibile eseguire i tipi di conversioni seguenti:

  • Conversioni implicite: non è richiesta alcuna sintassi speciale perché la conversione viene sempre eseguita e non si verificano perdite di dati. Gli esempi includono conversioni dai tipi integrali più piccoli ai più grandi e conversioni dalle classi derivate alle classi di base.

  • Conversioni esplicite (cast): le conversioni esplicite richiedono un'espressione cast. L'esecuzione di cast è obbligatoria se si prevede una possibile perdita di informazioni durante la conversione oppure se la conversione non riesce per altri motivi. Alcuni esempi tipici includono la conversione numerica in un tipo con precisione inferiore o con un intervallo più piccolo e la conversione di un'istanza della classe di base in una classe derivata.

  • Conversioni definite dall'utente: usano metodi speciali che possono essere definiti per abilitare conversioni esplicite e implicite tra tipi personalizzati che non includono una relazione tra classe di base e classe derivata. Per altre informazioni, vedere Operatori di conversione definiti dall'utente.

  • Conversioni con le classi helper: per eseguire la conversione tra tipi non compatibili, ad esempio numeri interi e oggetti System.DateTime oppure tra stringhe esadecimali e matrici di byte, è possibile usare la classe System.BitConverter, la classe System.Convert e i metodi Parse dei tipi numerici predefiniti, ad esempio Int32.Parse. Per altre informazioni, vedere Come convertire una matrice di byte in un int, Come convertire una stringa in un numero e Come eseguire la conversione tra stringhe esadecimali e tipi numerici.

Conversioni implicite

Per i tipi numerici predefiniti, è possibile eseguire una conversione implicita quando il valore da archiviare può essere adattato nella variabile senza essere troncato o arrotondato. Per i tipi integrali, questo significa che l'intervallo del tipo di origine è un subset appropriato dell'intervallo per il tipo di destinazione. Ad esempio, una variabile di tipo long (integer a 64 bit) può archiviare qualsiasi valore archiviabile da un tipo int (integer a 32 bit). Nell'esempio seguente il compilatore converte in modo implicito il valore num a destra di un tipo long prima di assegnarlo a bigNum.

// Implicit conversion. A long can
// hold any value an int can hold, and more!
int num = 2147483647;
long bigNum = num;

Per un elenco completo di tutte le conversioni numeriche implicite, vedere la sezione Conversioni numeriche implicite dell'articolo Conversioni numeriche predefinite.

Per i tipi di riferimento, è sempre disponibile una conversione implicita da una classe a qualsiasi classe di base diretta o indiretta o interfacce. Poiché una classe derivata contiene sempre tutti i membri di una classe di base, non è necessaria alcuna sintassi speciale.

Derived d = new Derived();

// Always OK.
Base b = d;

Conversioni esplicite

Tuttavia, se non è possibile eseguire una conversione senza il rischio di perdita di informazioni, il compilatore richiede di eseguire una conversione esplicita, nota come cast. Un cast consente di informare esplicitamente il compilatore che si intende eseguire la conversione e che si è a conoscenza della possibilità che si verifichi una perdita di dati o che il cast non riesca in fase di esecuzione. Per eseguire un cast, specificare tra parentesi il tipo di cui eseguire il cast prima del valore o della variabile da convertire. Il seguente programma esegue il cast di double in int. Il programma non verrà compilato senza il cast.

class Test
{
    static void Main()
    {
        double x = 1234.7;
        int a;
        // Cast double to int.
        a = (int)x;
        System.Console.WriteLine(a);
    }
}
// Output: 1234

Per un elenco completo delle conversioni numeriche esplicite supportate, vedere la sezione Conversioni numeriche esplicite dell'articolo Conversioni numeriche predefinite.

Per i tipi di riferimento, è necessario un cast esplicito se si vuole eseguire la conversione da un tipo di base a un tipo derivato:

// Create a new derived type.
Giraffe g = new Giraffe();

// Implicit conversion to base type is safe.
Animal a = g;

// Explicit conversion is required to cast back
// to derived type. Note: This will compile but will
// throw an exception at run time if the right-side
// object is not in fact a Giraffe.
Giraffe g2 = (Giraffe)a;

Un'operazione di cast tra tipi riferimento non modifica il tipo in fase di esecuzione dell'oggetto sottostante. Viene modificato solo il tipo del valore usato come riferimento a tale oggetto. Per altre informazioni, vedere Polimorfismo.

Eccezioni nella conversione di tipi in fase di esecuzione

In alcune conversioni di tipi riferimento il compilatore non può determinare se un cast è valido. È possibile che un'operazione di cast compilata correttamente non riesca in fase di esecuzione. Come illustrato nell'esempio seguente, un cast di tipo che non riesce in fase di esecuzione genera un'eccezione InvalidCastException.

class Animal
{
    public void Eat() => System.Console.WriteLine("Eating.");

    public override string ToString() => "I am an animal.";
}

class Reptile : Animal { }
class Mammal : Animal { }

class UnSafeCast
{
    static void Main()
    {
        Test(new Mammal());

        // Keep the console window open in debug mode.
        System.Console.WriteLine("Press any key to exit.");
        System.Console.ReadKey();
    }

    static void Test(Animal a)
    {
        // System.InvalidCastException at run time
        // Unable to cast object of type 'Mammal' to type 'Reptile'
        Reptile r = (Reptile)a;
    }
}

Il metodo Test ha un parametro Animal e pertanto il cast esplicito dell'argomento a a un oggetto Reptile formula un'ipotesi pericolosa. È più sicuro non formulare ipotesi, ma piuttosto controllare il tipo. Il linguaggio C# offre l'operatore is che consente di testare la compatibilità prima di eseguire effettivamente un cast. Per altre informazioni, vedere Come eseguire il cast in modo sicuro usando i criteri di ricerca e gli operatori as e is.

Specifiche del linguaggio C#

Per altre informazioni, vedere la sezione Conversioni della specifica del linguaggio C#.

Vedi anche