Исключения
Исключения используются в C# таким же образом, как и в C++, кроме двух следующих различий:
□ C# определяет блок finally
, который содержит код, всегда выполняющийся в конце блока try
независимо от того, порождалось ли какое-либо исключение. Отсутствие этого свойства C++ явилось причиной недовольства среди разработчиков C++. Блок finally
выполняется, как только управление покидает блок catch
или try
, и содержит обычно код очистки выделенных в блоке try
ресурсов.
□ В C++ класс, порожденный в исключении, может быть любым классом. C#, однако, требует, чтобы исключение было классом, производным от System.Exception
.
Правила выполнения программы в блоках try
и catch
идентичны в C++ и C#. Используемый синтаксис также одинаков, за исключением одного различия: в C# блок catch, который не определяет переменную для получения объекта исключения, обозначается самой инструкцией catch
. Синтаксис C++:
catch (...) {
Синтаксис C#.
catch {
В C# этот вид инструкции catch
может быть полезен для перехвата исключений, которые порождаются кодом, написанным на других языках (и которые поэтому могут не быть производными от System.Exception
, компилятор C# отметит ошибку, если попробовать определить такой объект-исключение, но это не имеет значения для других языков программирования).
Полный синтаксис для try…catch…finally
в C# выглядит следующим образом:
try {
// обычный код
} catch (MyException e) { // MyException выводится из System.Exception
// код обработки ошибки
}
// необязательные дополнительные блоки catch
finally {
// код очистки
}
Отметим, что блок finally
является необязательным. Также допустимо не иметь блоков catch
, в этом случае конструкция try…finally
служит просто способом обеспечения, чтобы код в блоке finally
всегда выполнялся, когда происходит выход из блока try
. Это может быть полезно, например, если блок try
содержит несколько инструкций return
и требуется выполнить очистку ресурсов, прежде чем метод реально возвратит управление.
Указатели и небезопасный код
Указатели в C# используются почти таким же образом, как и в C++. Однако они могут объявляться и использоваться только в блоке небезопасного (ненадежного) кода. Любой метод можно объявить небезопасным (unsafe
):
public unsafe void MyMethod() {
Можно альтернативно объявить любой класс или структуру небезопасными и:
unsafe class MyClass {
Объявление класса или структуры ненадежными означает, что все члены рассматриваются как ненадежные. Можно также объявить любое поле-член (но не локальные переменные) как ненадежное, если имеется поле-член типа указателя:
private unsafe int* рХ;
Можно также пометить блочный оператор как ненадежный следующим образом:
unsafe {
// инструкции, которые используют указатели
}
Синтаксис для объявления, доступа, разыменования и выполнения арифметических операций с указателями такой же, как и в C++:
// этот код будет компилироваться в C++ или C#
// и имеет одинаковый результат в обоих языках
int X = 10, Y = 20;
int *рХ = &Х;
*рХ = 30;
pХ = &Y;
++рХ; // добавляет sizeof(int) к рХ
Отметим, однако, следующие моменты.
□ В C# не допускается разыменовывать указатели void*
, также нельзя выполнять арифметические операции над указателями void*
. Синтаксис указателя void*
был сохранен для обратной совместимости, для вызова внешних функций API, которые не знают о .NET и которые требуют указателей void*
в качестве параметров.
□ Указатели не могут указывать на ссылочные типы (классы или массивы). Также они не могут указывать на структуры, которые содержат встроенные ссылочные типы в качестве членов. Это в действительности попытка защитить данные, используемые сборщиком мусора и средой выполнения .NET (хотя в C#, также как и в C++, если начать использовать указатели, почти всегда можно найти способ обойти любые ограничения, выполняя арифметические операции на указателях и затем разыменовывая их).
□ Помимо объявления соответствующих частей кода как ненадежных, необходимо также определять для компилятора флаг /unsafe
при компиляции кода, который содержит указатели.
□ Указатели не могут указывать на переменные, которые встроены в ссылочные типы данных (например, членов класса), если только они не объявлены внутри инструкции fixed
.
Фиксация донных в куче
Разрешается присвоить адрес типа данных значения указателю, даже если этот тип встроен как поле-член в ссылочный тип данных. Однако такой указатель должен быть объявлен внутри инструкции fixed
. Причина этого в том, что ссылочные типы могут в любое время перемещаться в куче сборщиком мусора. Сборщик мусора знает о ссылках C# и может обновить их, как требуется, но он не знает об указателях. Следовательно, если указатель направлен на член класса в куче и сборщик мусора перемещает весь экземпляр класса, то будет указан неправильный адрес. Инструкция fixed
не позволяет сборщику мусора перемещать указанный экземпляр класса во время выполнения блока fixed
, гарантируя целостность значений указателей.
class MyClass {
public int X; // и т.д.