Coding with Titans

so breaking things happens constantly, but never on purpose

Natywne API a .NET - P/Invoke - część II - Wskaźniki

Nieprawdą jest, że na platformie .NET nie istnieją wskaźniki. W C# w sekcji unsafe, możemy spokojnie mieszać “typowy” kod .NET z operacjami na wskaźnikach. Tu jednak uwaga:

  1. sekcje unsafe muszą być propagowane w górę, a to oznacza, że i metoda, w której są zawarte, musi być również opisana jako niebezpieczna (czasem i cała klasa),
  2. oraz co dużo ważniejsze - podczas kompilacji modułu (assembly) wymagany jest przełącznik “/unsafe”, zezwalający jawnie na używanie niebezpiecznych operacji na wskaźnikach.

Co ciekawe - dostępny staje się dobrze znany z C/C++ operator sizeof (a nie, jak wspomniano dotychczas tylko: Marshal.SizeOf() przy P/Invoke).

public unsafe void MakeTriple(int *pValue)
{
  *pValue = (*pValue) * 3;
}

UWAGA!

Wskaźniki są niebezpieczne przede wszystkim z racji istnienia śmieciarza w .NET (zwanego przez niektórych również Garbage Collectorem, w skrócie GC). Otóż ów GC analizuje użycie wszystkich obiektów utworzonych w czasie życia programu .NET-owego i decyduje, które z nich mają być usunięte z pamięci. Nie wdając się zbytnio w szczegóły procesu decyzyjnego, należy zauważyć, iż obiekt, na który wskazywał wskaźnik, może bez żadnego powiadomienia oraz jawnej przyczyny zostać usuniętym, a jego miejsce zupełnie nieużywane. GC może, co gorsze, rozpocząć również proces upakowywania obiektów (usunąć fragmentację pamięci), aby zrobić więcej dla nowych obiektów, a tym samym wskażnik nie będzie już pokazywal poprawnej lokalizacji, bądź wskazywał inny obiekt.

Typowe referencje (do klas lub struktur) nadążają za tą polityką i nie są powodem tego typu błedów. Aby więc zabezpieczyć się, przed owymi niepożądanymi skutkami, używane jest przypinanie obiektów, które nie pozwala GC na żadne manipulacje wskazanym obiektem. Służy do tego słowo kluczowe fixed.

public unsafe static void Main()
{
  MyData data = new MyData();

  fixed (int *ptr = &data.valueField)
  {
   *ptr = *ptr + 10;
  }
}