programming - Delphi levanta excepción en constructor
delphi technologies chihuahua (3)
SITUACIÓN
Voy a escribir una clase y el constructor es uno personalizado que he creado porque necesito inicializar algunos valores. Este es el código que he escrito hasta ahora:
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;
Como voy a hacer algunos cálculos matemáticos, debo evitar los números negativos.
PREGUNTA
¿Puedo plantear una excepción en el constructor de esa manera? Estoy ejecutando el código de esta manera:
procedure TForm1.Button1Click(Sender: TObject);
var a: TCombinatorio;
b: string;
begin
a := TCombinatorio.Create(5,-2);
try
//some code
finally
a.Free;
end;
end;
Como puede ver aquí, tengo parámetros incorrectos para mi constructor, ya que el segundo es negativo. Tampoco puedo entender (de acuerdo con el código de mi constructor) si realmente se necesita el a.Free
en el interior del a.Free
porque cuando el constructor genera la excepción, se llama al destructor.
Pensé incluir el a := TCombinatorio.Create(5,-2);
Dentro del bloque try-finally para evitar el problema pero no estoy seguro. ¿Qué piensas?
Bien, primero puede generar una excepción en el constructor, y sí, como consecuencia, llama al destructor. El código que muestra está bien. Pero creo que no entiendes lo que hace tu código. Y poner al constructor dentro de un bloque try sería finalmente incorrecto. El punto que creo que te falta es que si tu constructor falla el try...finally
bloque nunca se ejecuta y por lo tanto el libre no se ejecuta. No debe llamar gratis si el constructor no tiene éxito, por lo que no debe poner el constructor dentro del bloque try...finally
.
En primer lugar, diría que no se pueden evitar las excepciones en los constructores, por lo que no puede ser un antipatrón. Si verifica el código fuente de Delphi, encontrará una cantidad de lugares donde se genera una excepción en el constructor. Por ejemplo
constructor TCustomForm.Create(AOwner: TComponent);
begin
// ... skipped some lines
if not InitInheritedComponent(Self, TForm) then
raise EResNotFound.CreateFmt(SResNotFound, [ClassName]);
Lo único que debes saber es que Delphi llamará automáticamente al destructor si una excepción se escapa del constructor. En realidad, significa que su destructor puede ejecutarse en un objeto parcialmente construido y es su responsabilidad escribir el destructor correctamente. Consulte la documentación de TObject.Destroy y preste especial atención a la siguiente cita:
Nota: Si una excepción se escapa del constructor, se llama al destructor para destruir la instancia de objeto parcialmente construida que no se pudo inicializar completamente. Por lo tanto, los destructores deben verificar que los recursos asignados, como los identificadores, realmente se asignaron antes de intentar liberarlos, ya que su valor podría ser cero.
PD: En general, debe asumir que cada línea de código puede generar una excepción, pero no sea paranoico;)
Su código es absolutamente correcto y correcto. Aumentar las excepciones de los constructores es perfectamente respetable. Como sabéis se llama el destructor.
Preguntas sobre este código:
a := TCombinatorio.Create(5,-2);
try
//some code
finally
a.Free;
end;
Te preocupa que se llame a Free
después de que el objeto ya haya sido destruido. Eso no puede pasar. Si se genera una excepción en el constructor, se propaga a la pila de llamadas. Eso sucede antes de que comience el bloque try
y, por lo tanto, el bloque finally
no se ejecuta. De hecho, la asignación a a
no sucede.
Mover la creación dentro del try
sería desastroso y, de hecho, es un error increíblemente común. Supongamos que hiciste eso:
// WARNING THIS CODE IS DEFECTIVE
try
a := TCombinatorio.Create(5,-2);
//some code
finally
a.Free;
end;
Ahora, si se lanza una excepción, entonces se llama Free
pero ¿en qué? La variable a
no está inicializada. Incluso si lo fuera, y no lo es, todavía sería un doble gratis.