Delphi увеличивает исключение в конструкторе
СИТУАЦИЯ
Я собираюсь написать класс, а конструктор - мой, который я сделал, потому что мне нужно инициализировать некоторые значения. Это код, который я написал до сих пор:
type
TCombinatorio = class(TObject)
private
valN, valK: integer;
result: double;
public
property K: integer read valK;
property N: integer read valN;
constructor Create(valN: integer; valK: integer);
end;
constructor TCombinatorio.Create(valN: Integer; valK: Integer);
begin
inherited Create;
Self.valN := valN;
Self.valK := valK;
if ((valN < 0) or (valK < 0)) then
begin
raise Exception.Create('N and K must be >= 0');
end;
end;
Поскольку я собираюсь сделать некоторые математические вычисления, мне нужно избегать отрицательных чисел.
ВОПРОС
Могу ли я вызвать исключение в конструкторе таким образом? Я запускаю код следующим образом:
procedure TForm1.Button1Click(Sender: TObject);
var a: TCombinatorio;
b: string;
begin
a := TCombinatorio.Create(5,-2);
try
//some code
finally
a.Free;
end;
end;
Как вы можете видеть здесь, у меня неправильные параметры для моего конструктора, так как второй является отрицательным. Я также не могу понять (согласно коду моего конструктора), действительно ли необходим a.Free
внутри finally, потому что когда конструктор вызывает исключение, вызывается деструктор.
Я подумал включить a := TCombinatorio.Create(5,-2);
внутри блока try-finally, чтобы избежать проблемы, но я не уверен. Как вы думаете?
Ответы
Ответ 1
Ваш код является абсолютно точным и правильным. Получение исключений от конструкторов является вполне респектабельным. Как вы знаете, деструктор называется.
Вы спрашиваете об этом коде:
a := TCombinatorio.Create(5,-2);
try
//some code
finally
a.Free;
end;
Вы обеспокоены тем, что Free
будет вызван после того, как объект уже был уничтожен. Этого не может быть. Если в конструкторе возникает исключение, оно распространяется на стек вызовов. Это происходит до начала блока try
, поэтому блок finally
не выполняется. Действительно, присваивание a
не выполняется.
Перемещение создания внутри try
будет катастрофическим и на самом деле является невероятно распространенной ошибкой. Предположим, вы это сделали:
// WARNING THIS CODE IS DEFECTIVE
try
a := TCombinatorio.Create(5,-2);
//some code
finally
a.Free;
end;
Теперь, если возникает исключение, вызывается Free
, но на что? Переменная a
не инициализируется. Даже если бы это было, а это не так, это все равно было бы бесплатным.
Ответ 2
ОК, сначала вы можете создать исключение в конструкторе, и да, как следствие, он вызывает деструктор. Код, который вы показываете, в порядке. Но я думаю, вы неправильно поняли, что делает ваш код. И поставить конструктор внутри блока finally try будет неправильным. То, что я думаю, что вам не хватает, заключается в том, что если ваш конструктор не работает, блок try...finally
никогда не запускается, и поэтому бесплатный не выполняется. Вы не должны звонить бесплатно, если конструктор не удался, поэтому вы не должны помещать конструктор внутри блока try...finally
.
Ответ 3
Прежде всего, я бы сказал, что вы не можете избежать исключений в конструкторах, поэтому он не может быть анти-шаблоном. Если вы проверите исходный код Delphi, вы найдете количество мест, в которых исключение возникает в конструкторе. Например
constructor TCustomForm.Create(AOwner: TComponent);
begin
// ... skipped some lines
if not InitInheritedComponent(Self, TForm) then
raise EResNotFound.CreateFmt(SResNotFound, [ClassName]);
Единственное, что вы должны знать, это то, что Delphi автоматически вызовет деструктор, если исключение выйдет из конструктора. На самом деле это означает, что ваш деструктор может быть выполнен на частично построенном объекте, и ваша ответственность - правильно писать деструктор. Смотрите документацию TObject.Destroy и обратите особое внимание на приведенную ниже цитату:
Примечание. Если исключение выходит из конструктора, деструктор вызывается для уничтожения частично сконструированного экземпляра объекта, который не удалось полностью инициализировать. Поэтому деструкторы должны проверять что выделенные ресурсы, такие как ручки, были фактически распределены прежде чем пытаться их освободить, поскольку их значение может быть нулевым.
PS Вообще вы должны предположить, что каждая строка кода может вызвать исключение, но, пожалуйста, не параноик;)