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 del operador is. El casting se define como el uso del operador de cast.
En C#, muy a menudo, al margen de los valores numéricos y las jerarquías de clases, tendrás que hacer una conversión llamando a algún miembro del tipo de destino, como Int32.Parse(), que convierte un string en un entero, o llamando a un miembro del tipo de origen, por ejemplo object.ToString(). Los desarrolladores de JavaScript y de otros lenguajes dinámicos deberían tenerlo en cuenta.
Ten en cuenta que hay disponibles operadores de conversión implícita y explícita (que se tratan en operator-overloading), lo que permite llevar un casting bastante arbitrario 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 produce entre dos tipos numéricos siempre que el tipo de destino pueda conservar la escala y el signo del valor del tipo de origen. Fíjate en la excepción que se indica en 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 fin.
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 de destino no puede representar el valor que se está convirtiendo porque no es lo bastante amplio o hay un conflicto de signo, entonces puede lanzarse una excepción de desbordamiento en el caso de los enteros, o la variable del tipo de destino puede tomar un valor Infinity en el caso de los float y los double.
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 interfaz.
IList<int> ll = new List<int>();
Se puede usar un operador de cast para convertir una clase base en 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 está convirtiendo 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; en caso contrario, se le asignará ifoo.
Los tipos pueden definir sus propios operadores de cast explícitos e implícitos.
Un ejemplo de conversiones explícitas e implícitas en la BCL son las conversiones desde el struct BigInteger hacia otros tipos numéricos y viceversa.
typeof
Si necesitas detectar el tipo exacto de un objeto, is puede ser demasiado permisivo, ya que convertirá un objeto a una clase o a cualquiera de sus clases base. En ese caso, la solución es 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