C# - Diferencia entre usar Convert y usar cast analizada usando un decompiler
Intermedio
Esta es una duda frecuente cuando quieres convertir de un tipo numérico a otro.
En ese caso hay diferencias importantes sobre todo en cuanto a velocidad de procesamiento, veamos dos ejemplos que he explicado después de analizar los ensamblados del .Net Framework para revisar su funcionamiento interno..
Convertir de tipo decimal a tipo int
Utilizando cast
[ int a = (int)123.33m
]
El runtime lo que hará es simplemente tomar la parte entera del número descartando completamente la parte decimal.
El tipo decimal es de 16 bytes mientras que el tipo int es de 4 bytes, lo que sucede es que el runtime toma la parte entera del tipo decimal y la coloca en los 4 bytes del int.
Pero atención, siempre y cuando la parte entera del dato tipo decimal quepa en 2^32 , de lo contrario se genera una excepción de overflow.
Utilizando Convert.ToInt32
Cuando el método estático Convert.ToInt32() (en adelante solo ToInt32) recibe como parámetro un tipo de dato decimal hace más que solo retornar la parte entera, de hecho redondea el resultado al entero más cercano.
Esto genera una sobrecarga adicional que lo hace mucho más lento.
Cuando ToInt32 recibe un tipo decimal llama internamente al método Decimal.FCallToInt32 que a su vez es un wrapper de un método implementado en código nativo que hace los cálculos necesarios para producir un valor redondeado.
Como se observa entonces hay invocación a 3 métodos como mínimo lo cual implica 3 cambios de contexto: el primero al llamar a ToInt32, luego al llamar a Decimal.FCallToInt32 y luego cuando este hace el wrapper a la función de código nativo, hasta allí ya es mucho más lento que simplemente usar el cast (int) .
Ahora, la lógica usada internamente para convertir el tipo de 16 bytes al tipo de 4 bytes y tener en cuenta el redondeo y los casos especiales como por ejemplo cuando el parámetro es null, convierte a esta función en una muchísimas veces más lenta que solo hacer cast a int.
Convertir de tipo float a tipo int
Utilizando cast
[ int a = (int)123.33f
]
El runtime lo que hará es simplemente tomar la parte entera del número descartando completamente la parte decimal.
El tipo float es de 4 bytes al igual que el tipo int, así que lo que sucede es que el runtime toma la parte entera del tipo float y la coloca en los 4 bytes enteros.
Utilizando Convert.ToInt32
Cuando el método estático ToInt32() recibe como parámetro un tipo de dato float hace más que solo retornar la parte entera, de hecho redondea el resultado al entero más cercano. Esto genera una sobrecarga adicional que lo hace mucho más lento.
ToInt32 cuando recibe un tipo float llama al método interno Convert.ToInt32 (se llama sí mismo, pero llama la spbrecarga que recibe parámetro tipo double ya que la lógica para double es la misma que para float) que es finalmente quien resuelve la lógica para hacer el redondeo.
Como se observa entonces hay invocación a 2 métodos mínimo lo cual implica 2 cambios de contexto: el primero al llamar a ToInt32(float) , luego al llamar a ToInt32(double) que al final es que tiene en cuenta el redondeo y los casos especiales como por ejemplo cuando el parámetro es null.
Esto convierte a esta función en una muchísimas veces más lenta que solo hacer cast a int, pero comparativamente con el caso de decimal no es tan lenta.
Conclusión
Si necesitas velocidad siempre usa operadores de cast ejemplo:
int a = (int)123.33f
Si requieres aproximación y que te tengan en cuenta los casos especiales usa siempre Convert.
int a = Convert.ToInt32(123.33f);