technologies programming mexico greece empresa chihuahua autopartes delphi

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.