El casting y la conversión de tipos son formas distintas de cambiar una expresión de un tipo de datos a otro.
La documentación de C# clasifica la conversión de tipos como el uso del operador as o el operador is. El casting se define como el uso del operador de cast.
En C#, muy a menudo, fuera del ámbito de los valores numéricos y de las jerarquías de clases, tendrás que hacer una conversión llamando a algún miembro del tipo «to», como Int32.Parse(), que convierte un string en un entero, o llamando a un miembro del tipo «from», por ejemplo object.ToString(). Quienes programan en JavaScript y en otros lenguajes dinámicos deben tenerlo en cuenta.
Ten en cuenta que hay operadores de cast implícito y explícito (que se tratan en operator-overloading) que te permiten aplicar un casting bastante libre a tus propios tipos.
El sistema de tipos de C# es algo más estricto que el de C o el de JavaScript y, en consecuencia, las operaciones de casting están más restringidas. El casting implícito se da entre dos tipos numéricos siempre que el tipo «to» pueda conservar la magnitud y el signo del valor del tipo «from». Fíjate en la excepción que señala la documentación para la conversión a números reales, donde se puede perder precisión.
Un cast implícito no se indica con ninguna sintaxis especial. Por ejemplo:
int myInt = 1729;
long myLong = myInt;
No hay conversión implícita de una expresión numérica (ni de un string) a bool. La Biblioteca de Clases Base (BCL) proporciona Convert.ToBoolean() para este propósito.
Una expresión de tipo char se puede convertir implícitamente a int. El cast en la dirección opuesta tiene que ser explícito. No todos los valores de int son caracteres UTF-16 válidos.
Cuando los tipos numéricos no se pueden convertir implícitamente, por lo general puedes usar el operador de cast explícito.
Cuando el tipo «to» no puede representar el valor que se está convirtiendo porque no es lo bastante amplio o hay un conflicto de signo, en el caso de los enteros se puede lanzar una excepción de desbordamiento, y en el caso de los float y los double la variable del tipo «to» puede tomar un valor de Infinity.
Una expresión de tipo int se puede convertir explícitamente a char. Esto puede dar como resultado un char no válido.
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
Consulta este artículo para saber más sobre la palabra clave checked.
Cualquier tipo se puede convertir implícitamente a su clase base o a su interfaz.
IList<int> ll = new List<int>();
Se puede usar un operador de cast para convertir de una clase base a una clase derivada.
IList<int> ll = new List<int>();
List<int> l = (List<int>)ll;
Si el cast falla porque el tipo de la expresión que se convierte no está relacionado con el tipo al que se convierte, se lanza una instancia de InvalidCastException.
Un enfoque más habitual para estas conversiones de tipos es usar la palabra clave 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 palabra clave as cumple una función similar a is, por ejemplo var foo = ifoo as Foo;. En este ejemplo, foo será null si ifoo no es de tipo Foo; de lo contrario, se le asignará ifoo.
Los tipos pueden definir sus propios operadores de cast explícitos e implícitos personalizados.
Un ejemplo de casts explícitos e implícitos en la BCL son las conversiones desde y hacia otros tipos numéricos con la estructura BigInteger.
typeof
Si necesitas detectar el tipo exacto de un objeto, is puede ser un poco demasiado permisivo, ya que convertirá un objeto a una clase o a cualquiera de sus clases base. En ese caso, la solución son typeof y Object.GetType().
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