Чтение онлайн

ЖАНРЫ

Язык программирования C#9 и платформа .NET5
Шрифт:

Если вы решили задействовать данное средство языка С#, тогда придется информировать компилятор C# о своих намерениях, разрешив проекту поддерживать "небезопасный код". Создайте новый проект консольного приложения по имени

UnsafeCode
и включите поддержку небезопасного кода, добавив в файл
UnsafeCode.csproj
следующие строки:

<PropertyGroup>

<AllowUnsafeBlocks>true</AllowUnsafeBlocks>

</PropertyGroup>

Для установки

этого свойства в Visual Studio предлагается графический пользовательский интерфейс. Откройте окно свойств проекта. В раскрывающемся списке Configuration (Конфигурация) выберите вариант All Configurations (Все конфигурации), перейдите на вкладку Build (Сборка) и отметьте флажок Allow unsafe code (Разрешить небезопасный код), как показано на рис. 11.1.

Ключевое слово unsafe

Для работы с указателями в C# должен быть специально объявлен блок "небезопасного кода" с использованием ключевого слова

unsafe
(любой код, который не помечен ключевым словом
unsafe
, автоматически считается "безопасным"). Например, в следующем файле
Program.cs
объявляется область небезопасного кода внутри операторов верхнего уровня:

using System;

using UnsafeCode;

Console.WriteLine("***** Calling method with unsafe code *****");

unsafe

{

// Здесь работаем с указателями!

}

// Здесь работа с указателями невозможна!

В дополнение к объявлению области небезопасного кода внутри метода можно строить "небезопасные" структуры, классы, члены типов и параметры. Ниже приведено несколько примеров (типы

Node
и
Node2
в текущем проекте определять не нужно):

// Эта структура целиком является небезопасной и может

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

unsafe struct Node

{

public int Value;

public Node* Left;

public Node* Right;

}

// Эта структура безопасна, но члены Node2* - нет.

// Формально извне небезопасного контекста можно

// обращаться к Value, но не к Left и Right.

public struct Node2

{

public int Value;

// Эти члены доступны только в небезопасном контексте!

public unsafe Node2* Left;

public unsafe Node2* Right;

}

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

что какой-то статический метод будет использовать логику указателей. Чтобы обеспечить возможность вызова данного метода только из небезопасного контекста, его можно определить так:

static unsafe void SquareIntPointer(int* myIntPointer)

{

// Возвести значение в квадрат просто для тестирования.

*myIntPointer *= *myIntPointer;

}

Конфигурация метода требует, чтобы вызывающий код обращался к методу

SquareIntPointer
следующим образом:

unsafe

{

int myInt = 10;

// Нормально, мы находимся в небезопасном контексте.

SquareIntPointer(&myInt);

Console.WriteLine("myInt: {0}", myInt);

}

int myInt2 = 5;

// Ошибка на этапе компиляции!

// Это должно делаться в небезопасном контексте!

SquareIntPointer(&myInt2);

Console.WriteLine("myInt: {0}", myInt2);

Если вы не хотите вынуждать вызывающий код помещать такой вызов внутрь небезопасного контекста, то можете поместить все операторы верхнего уровня в блок

unsafe
. При использовании в качестве точки входа метода
Main
можете пометить
Main
ключевым словом
unsafe
. В таком случае приведенный ниже код скомпилируется:

static unsafe void Main(string[] args)

{

int myInt2 = 5;

SquareIntPointer(&myInt2);

Console.WriteLine("myInt: {0}", myInt2);

}

Запустив такую версию кода, вы получите следующий вывод:

myInt: 25

На заметку! Важно отметить, что термин "небезопасный" был выбран небезосновательно. Прямой доступ к стеку и работа с указателями может приводить к неожиданным проблемам с вашим приложением, а также с компьютером, на котором оно функционирует. Если вам приходится иметь дело с небезопасным кодом, тогда будьте крайне внимательны.

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

После установления небезопасного контекста можно строить указатели и типы данных с помощью операции

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

Поделиться с друзьями: