C# es un lenguaje seguro por defecto, lo que significa que evita errores comunes como accesos ilegales a memoria. Sin embargo, hay ocasiones en las que necesitamos un control más bajo nivel, como en interoperabilidad con código nativo o para optimizaciones de rendimiento. Para eso existe la palabra clave unsafe
La palabra clave unsafe en C# habilita el uso de punteros, acceso directo a memoria y operaciones que normalmente están fuera del alcance del entorno de ejecución administrado del .NET runtime. En otras palabras, permite usar características similares a C/C++, con sus pros y contras.
Para usarlo, debés marcar bloques, métodos o estructuras con la palabra clave unsafe. También tenés que habilitarlo en el proyecto.
En el `.csproj`:
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
Veamos un ejemplo:
unsafe
{
int valor = 42;
int* puntero = &valor;
Console.WriteLine(*puntero); // Imprime 42
}
Cuando queremos apuntar a arrays o strings, que son gestionados por el Garbage Collector, necesitamos usar fixed para evitar que el objeto se mueva en memoria:
unsafe
{
int[] numeros = { 10, 20, 30 };
fixed (int* p = numeros)
{
Console.WriteLine(p[1]); // Imprime 20
}
}
Que podemos hacer con unsafe?
- Declaración y uso de punteros (`*`, `&`)
- Indexación de punteros (`p[i]`)
- Aritmética de punteros (`p++`, `p + 1`, etc.)
- Conversión entre tipos de punteros
- Uso de `sizeof(T)` para tipos primitivos
Pero ¿Cuándo usamos unsafe?
- Interop con librerías nativas (C, C++, DLLs).
- Manipulación avanzada de memoria.
- Procesamiento de imágenes o buffers donde la performance es crítica.
- Serialización binaria de alto rendimiento.
Como se pueden imaginar esto es tan util como peligroso, que es lo que puede salir mal?
- Podemos introducir errores difíciles de depurar (como corrupción de memoria).
- Desactiva algunas protecciones del CLR.
- El código unsafe no se ejecuta en entornos con restricciones de seguridad (como ciertos sandboxes).
- No es portable entre arquitecturas de forma garantizada.
Dentro de unsafe, podemos usar sizeof` para obtener el tamaño de tipos primitivos:
unsafe
{
Console.WriteLine(sizeof(int)); // 4
Console.WriteLine(sizeof(byte)); // 1
}
También podés usarlo con nint, nuint, float, double, etc.
Si queremos evitar unsafe pero aún así trabajar con memoria de forma eficiente:
- Span<T>` y `Memory<T>
- System.Runtime.InteropServices.MemoryMarshal
- stackalloc para asignaciones rápidas en la pila (sin punteros).
El modo unsafe en C# te da acceso a un poder inmenso, pero con gran responsabilidad. Es una herramienta útil para situaciones específicas donde el rendimiento o la interoperabilidad lo justifican, pero debe usarse con precaución y conocimiento.