Глава.20 .Исключения.и.управление.состоянием
private sealed class Type1 {
static Type1() {
// В случае исключения M не вызывается
Console.WriteLine("Type1's static ctor called");
}
public static void M() { }
}
Вот результат работы этого кода:
In try
Type1's static ctor called
Нам нужно, чтобы код в блоке
try
выполнялся только при условии выполнения
кода в связанных с ним блоках
catch
и
finally
. Для этого код следует переписать
так:
private static void Demo2() {
// Подготавливаем код в блоке finally
RuntimeHelpers.PrepareConstrainedRegions(); // Пространство имен
// System.Runtime.CompilerServices
try {
Console.WriteLine("In try");
}
finally {
// Неявный вызов статического конструктора Type2
Type2.M();
}
}
public class Type2 {
static Type2() {
Console.WriteLine("Type2's static ctor called");
}
// Используем атрибут, определенный в пространстве имен
// System.Runtime.ConstrainedExecution
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
public static void M() { }
}
После запуска этой версии кода получаем:
Type2's static ctor called
In try
Метод
PrepareConstrainedRegions
играет особую роль. Обнаружив его перед
блоком
try
, JIT-компилятор немедленно начинает компилировать соответству-
ющие блоки
catch
и
finally
. JIT-компилятор загружает любые сборки, создает
любые типы, вызывает любые статические конструкторы и компилирует любые
методы. Если хотя бы одна из этих операций дает сбой, исключение возникает
до
входа потока в блок
try
.
545
Области.ограниченного.выполнения
В процессе подготовки методов JIT-компилятор просматривает весь граф вы-
зовов. Однако обрабатывает он только методы, к которым был применен атрибут
ReliabilityContractAttribute
со значениями параметра
Consistency
равными
WillNotCorruptState
или
Consistency.MayCorruptInstance
, так как для методов,
которые могут повредить домен приложений или состояние процесса, CLR не
дает никаких гарантий. Нужно гарантировать, что внутри защищаемого методом
PrepareConstrainedRegions
блока
catch
или
finally
будут вызываться только
методы с настроенным, как показано в предыдущем фрагменте кода, атрибутом
ReliabillityContractAttribute
.
Вот как выглядит этот атрибут:
public sealed class ReliabilityContractAttribute : Attribute {
public ReliabilityContractAttribute(
Consistency consistencyGuarantee, Cer cer);
public Cer Cer { get; }
public Consistency ConsistencyGuarantee { get; }
}
Он дает разработчику возможность указать степень надежности отдельного
метода
1
. Типы
Cer
и
Consistency
относятся к перечислениям и определяются
следующим образом:
enum Consistency {
MayCorruptProcess, MayCorruptAppDomain,
MayCorruptInstance, WillNotCorruptState
}
enum Cer { None, MayFail, Success }
Если ваш метод гарантированно не может повредить состояние, используйте
значение
Consistency.WillNotCorruptState
. В противном случае выберите одно
из трех других значений в зависимости от степени его надежности. Для метода
с гарантированно успешным завершением используйте значение
Cer.Success
.
В противном случае выбирайте параметр
Cer.MayFail
. Любой метод, для которого
не определен атрибут
ReliabilityContractAttribute
, можно считать помеченным
таким вот образом:
[ReliabilityContract(Consistency.MayCorruptProcess, Cer.None)]
Значение
Cer.None
указывает, что никаких CER-гарантий в данном случае не
дается. Другими словами, метод может завершиться неудачно и даже не сообщить
об этом. Помните, что большинство этих параметров дают методу возможность
проинформировать вызывающую сторону, чего от него можно ожидать. CLR и JIT-
компилятор эти сведения игнорируют.
Чтобы написать надежный метод, делайте его как можно меньше по размеру
и ограничивайте сферу его действия. Убедитесь, что там не выделяется память под
1
Атрибут можно применить также к интерфейсу, конструктору, структуре, классу или
сборке.
546
Do'stlaringiz bilan baham: |