Le mot-clé unsafe
en C# permet de manipuler directement la mémoire à l'aide de pointeurs, ce qui est généralement interdit en programmation managée. Il existe pour :
- Optimiser les performances : Accès direct à la mémoire peut être plus rapide dans certains cas.
- Interopérabilité : Facilite l'intégration avec du code natif en C/C++.
- Manipuler des structures mémoire spécifiques : Utile pour le traitement bas-niveau, comme les moteurs de jeu ou certaines applications système.
Cependant, l'utilisation du code unsafe
désactive certaines protections offertes par le garbage collector ou le compilateur et peut entraîner des erreurs mémoire difficiles à déboguer.
Avec unsafe
, C# permet :
Opérateur/Instruction | Utilisation |
---|---|
* |
Déférencement d'un pointeur. |
-> |
Accès à un membre d'une structure via un pointeur. |
[] |
Indexation d'un pointeur. |
& |
Obtention de l'adresse mémoire d'une variable. |
++ et -- |
Incrémentation et décrémentation des pointeurs. |
+ et - |
Opérations arithmétiques sur les pointeurs. |
== , != , < , > , <= , >= |
Comparaison de pointeurs. |
stackalloc |
Allocation de mémoire sur la pile. |
fixed |
Fixe temporairement une variable pour obtenir son adresse. |
unsafe
{
struct Point { public int X, Y; }
Point p = new Point { X = 10, Y = 20 };
Point* ptr = &p;
Console.WriteLine(ptr->X); // Accès via ->
}
Unsafe.As<TFrom, TTo>(ref TFrom source)
: Effectue un cast sans vérification.Unsafe.Read<T>(void* source)
: Lit un objet de la mémoire.Unsafe.Write<T>(void* destination, T value)
: Écrit un objet en mémoire.Unsafe.SizeOf<T>()
: Retourne la taille en mémoire d'un type.Unsafe.CopyBlock(void* destination, void* source, uint byteCount)
: Copie des blocs de mémoire.
int valeur = 42;
float casted = Unsafe.As<int, float>(ref valeur);
Console.WriteLine(casted); // Cast sans conversion (5.9E-44)
int valeur = 42;
float casted = Unsafe.As<int, float>(ref valeur);
Console.WriteLine(casted); // Cast sans conversion (5.9E-44)
L'utilisation des pointeurs permet de contourner certaines vérifications d'accès aux tableaux, améliorant les performances.
unsafe
{
int[] array = { 1, 2, 3, 4, 5 };
fixed (int* ptr = array)
{
int* p = ptr;
for (int i = 0; i < array.Length; i++)
{
Console.WriteLine(*(p + i)); // Accès direct sans vérification de bornes
}
}
}
unsafe
{
int* tableau = stackalloc int[5];
for (int i = 0; i < 5; i++)
{
*(tableau + i) = i * 2;
Console.WriteLine(*(tableau + i));
}
}
using System;
using System.Runtime.CompilerServices;
unsafe
{
byte[] source = { 1, 2, 3, 4, 5 };
byte[] destination = new byte[5];
fixed (byte* srcPtr = source, destPtr = destination)
{
Unsafe.CopyBlock(destPtr, srcPtr, (uint)source.Length);
}
Console.WriteLine(string.Join(", ", destination)); // 1, 2, 3, 4, 5
}
unsafe
{
struct Vector2
{
public float X;
public float Y;
}
Vector2 vec = new Vector2 { X = 3.5f, Y = 7.2f };
Vector2* ptr = &vec;
Console.WriteLine($"X: {ptr->X}, Y: {ptr->Y}");
}
unsafe
{
int[] tableau = { 10, 20, 30, 40, 50 };
fixed (int* ptr = tableau)
{
*(ptr + 2) = 100; // Modification de l'élément à l'index 2
}
Console.WriteLine(string.Join(", ", tableau)); // 10, 20, 100, 40, 50
}
Le code unsafe
en C# est un outil puissant pour les cas nécessitant un accès mémoire direct. Il doit être utilisé avec précaution car il introduit des risques liés à la gestion manuelle de la mémoire, mais il est utile pour des scénarios exigeant des performances optimales ou une interopérabilité avec du code natif.