// и может использоваться только в небезопасном контексте.

public unsafe struct Node {

 public int Value;

 public Node* Left;

 public Node* Right;

}

// Эта структура является безопасной, но члены Node* – нет.

// Строго говоря, получить доступ к 'Value' извне небезопасного

// контекста можно, а к 'Left' и 'Right' - нет.

public struct Node {

 public int Value;

 // К этим элементам можно получить доступ только

 // в небезопасном контексте!

 public unsafe Node* Left;

 public unsafe Node* Right;

}

Методы (как статические, так и уровня экземпляра) тоже можно обозначать, как небезопасные. Предположим, например, вы знаете, что некоторый статический метод использует логику указателей. Чтобы гарантировать вызов данного метода только в небезопасном контексте, можно определить метод так, как показано ниже.

unsafe public static void SomeUnsafeCode {

 // Операторы для работы о указателями.

}

В такой конфигурации требуется, чтобы вызывающая сторона обращалась к SomeUnsafeCode так.

static void Main(string[] args) {

 unsafe {

  SomeUnsafeCode;

 }

}

Если же не обязательно, чтобы вызывающая сторона делала вызов в небезопасном контексте, то можно не указывать ключевое слово unsafe в методе SomeUnsafeCode и записать следующее:

public static void SomeUnsafeCode {

 unsafe {

  // Операторы для работы с указателями.

 }

}

что должно упростить вызов:

static void Main(string[] args) {

 SomeUnsafeCode;

}

Работа с операциями * и &

После создания небезопасного контекста вы можете строить указатели на типы с помощью операции * и получать адреса заданных указателей с помощью операции &. В C# операция * применяется только к соответствующему типу, а не как префикс ко всем именам переменных указателя. Например, в следующем фрагменте программного кода объявляются две переменные типа int* (указатель на целое).

// Нет! В C# это некорректно!

int *pi, *pj;

// Да! Это в C# правильно.

int* pi, pj;

Рассмотрим следующий пример.

unsafe {

 int myInt;

 // Определения указателя типа int

 // и присваивание ему адреса myInt.

 int* ptrToMyInt = &myInt

 // Присваивание значения myInt

 // с помощью разыменования указателя.

 *ptrToMyInt = 123;

 // Печать статистики.

 Console.WriteLine("Значение myInt {0}", myInt);

 Console.WriteLine("Адрес myInt {0:X}", (int)&ptrToMyInt);

}

Небезопасная (и безопасная) функция Swap

Конечно, объявление указателей на локальные переменные с помощью просто-то присваивания им значений (как в предыдущем примере) никогда не требуется. Чтобы привести более полезный пример небезопасного программного кода, предположим, что вы хотите создать функцию обмена, используя арифметику указателей.

unsafe public static void UnsafeSwap(int* i, int* j) {

 int temp = *i;

 *i = *j;

 *j = temp;

}

Очень похоже на C, не так ли? Однако с учетом знаний, полученных из главы 3, вы должны знать, что можно записать следующую безопасную версию алгоритма обмена, используя ключевое слово C# ref.

public static void SafeSwap(ref int i, ref int j)

 int temp = i;

 i = j;

 j = temp;

}

Функциональные возможности каждого из этих методов идентичны, и это еще раз подтверждает, что работа напрямую с указателями в C# требуется редко. Ниже показана логика вызова,

static void Main(string[] args) {

 Console.WriteLine(*** Вызов метода с небезопасным кодом ***");

 // Значения для обмена.

 int i = 10, i = 20;

 // 'Безопасный' обмен значениями.

 Console.WriteLine("\n***** Безопасный обмен *****");

 Cоnsоle.WriteLine("Значения до обмена: i = {0}, j = {1}", i, j);

 SafeSwap(ref 1, ref j);

Перейти на страницу:

Похожие книги