Java具有finalize块,允许在块之后执行一些语句 保留(即使引发异常也会执行)。例如:
try {
...
} catch (Exception e) {
...
} finally {
... // any code here
}
Ada拥有受控对象,可以实现 Finalize 操作 但是没有与java中相同的finalize块。这对于日志记录非常有用 关闭文件,事务等(无需为每个可能的块创建特定的标记类型)。
答案 0 :(得分:2)
正如Adrien在评论中提到的那样,Finalize
更类似于析构函数。
为了得到一些近似异常/最终序列的东西你可以沿着这些方向做一些事情(警告,不编译,只键入 - 我们将一起解决任何错误:-)参见Ada RM Exceptions section。
with Ada.Exceptions; use Ada.Exceptions;
procedure Do_Something is
-- Variables and what-not...
-- In case you have an exception and want to reraise it after you've done
-- the 'final' processing.
Exception_Caught : Exception_Occurrence := Null_Occurrence;
begin
-- You can have some statements, like initializations, here that will not
-- raise exceptions. But you don't have to, it can all just go in the
-- following block. However you want to do it...
declare
-- If you need to declare some entities local to a block, put those here.
-- If not, just omit this declare section. Be aware, though, that if
-- you initialize something in here and it raises an exception, the
-- block's exception handler will not catch it. Such an exception will
-- propagate out of the whole procedure (unless it has an outermost
-- exception handler) because you're _not_ in the block's scope yet.
begin
-- Main processing that might raise an exception
...
exception
when E : others =>
-- Handle any exception that's raised. If there are specific
-- exceptions that can be raised, they should be explicitly
-- handled prior to this catch-all 'others' one.
-- Save the exception occurrence, i.e. make a copy of it that can
-- be reraised in the 'Final' section if needed. (If you want to
-- reraise for a specific exception, do this in those handlers as
-- well.
Save_Occurrence(Exception_Caught, E);
end;
-- Final processing. Everything from here to the end of the procedure is
-- executed regardless of whether an exception was raised in the above
-- block. By it including an others handler, it ensured that no exception
-- will propagate out of this procedure without hitting this 'Final' code.
-- If an exception was raised and needs to be propagated:
if Exception_Caught /= Null_Occurrence then
Reraise_Exception(Exception_Caught);
end if;
end Do_Something;
答案 1 :(得分:2)
我相信这段代码会按你的要求行事;它使用当前42
或raise
成功打印出return
。这是T.E.D建议的实施。
在Mac OS X,Darwin 10.6.0上使用GCC 4.5.0进行测试。
with Ada.Finalization;
package Finally is
-- Calls Callee on deletion.
type Caller (Callee : not null access procedure)
is new Ada.Finalization.Limited_Controlled with private;
private
type Caller (Callee : not null access procedure)
is new Ada.Finalization.Limited_Controlled with null record;
procedure Finalize (Object : in out Caller);
end Finally;
package body Finally is
procedure Finalize (Object : in out Caller)
is
begin
Object.Callee.all;
end Finalize;
end Finally;
with Ada.Text_IO; use Ada.Text_IO;
with Finally;
procedure Finally_Demo is
begin
declare
X : Integer := 21;
-- The cleanup procedure, to be executed when this block is left
procedure F
is
begin
Put_Line ("X is " & Integer'Image (X));
end F;
-- The controlled object, whose deletion will execute F
F_Caller : Finally.Caller (F'Access);
begin
X := 42;
raise Constraint_Error;
end;
end Finally_Demo;
答案 2 :(得分:2)
假设你已经理解了ada.finalization和java中的finalize块之间的区别,我会做类似下面的事情,它应该具有相同的效果。
procedure x is
begin
-- some code
begin
-- more code (your try)
exception
-- handle exception if necessary (caught exception)
end;
-- yet more code which is executed regardless of any exception handling.
end x;
答案 3 :(得分:1)
Marc C有正确的方法试图在直线程序代码中模拟它。
然而,恕我直言,这种结构主要是一种破解Java的面向对象系统的方法,对于那些希望在老式程序编程中具有OO结构优势的人来说。即使在Java中,你也总是更好地创建一个合适的类。
所以我认为在Ada中获得该功能的正确方法是制作一个合适的对象,并使你的对象成为Ada.Finalization.Controlled
的孩子,这并不是一件容易的事。< / p>
如果你不想打扰创建一个实际的对象,你可以创建一个虚拟对象,将你的终结代码放入其中,然后在你希望它运行的块顶部的堆栈上声明它。缺点是受控类型本身(至少在我上次使用它们时)必须在包级别范围内声明。在这种情况下,您将无法直接引用其中较低声明的对象。他们声称他们将在未来的语言版本中修复它,但我最近没有尝试过它,看看是否有。
答案 4 :(得分:1)
想到另一个答案。它有点沉重(也许比它的价值更麻烦)。但它会给你一些看起来有点像旧的最终块
的东西我们的想法是将您的“可终结”代码放入任务中。在任务终止之前,您不能离开作用域的任务。因此,您可以将工作代码放在任务中,并将“最终”代码放在任务定义范围之外。父任务将坐在那里等待工作任务结束(以某种方式),然后它会运行“最终”代码,无论它如何结束。缺点是如果任务抛出异常,它将在任务处停止。因此,您仍然不会获得可以抛出异常的行为,并且在“finalize”代码运行时它会自动传播出去。你可以通过添加一个集合点和第二个任务来解决这个问题(这就是任务的问题。它们就像薯片......你总是需要一个)。
procedure Finalized is
begin
declare
task Worker is end Worker;
task body Worker is begin
--// Working code in here. Can throw exceptions or whatever.
--// Does not matter.
end Worker;
begin
end;
--// If we get here, we know the task finished somehow (for good or ill)
--// so the finalization code goes here.
end Finalized;
在我看来,也许有一种方法可以用受保护的对象做这样的事情。我会留下那个让其他人知道的。
答案 5 :(得分:0)
让我们正视这个问题。
在编程理论中存在对象的创建和销毁以及过程的尝试和最后的概念。两者最终都处理资源管理,但它们的重点不同。
现在,根据过程,我们可能需要生成一系列资源,如果过程被致命错误中断,则必须以相反的顺序回滚所有生成的资源。通常,这是通过创建专用于管理各自资源的对象来最简单地实现的。 Ada.Finalization 在这里变得非常有用。
但这可能有点矫枉过正,并且可能会根据具体情况对这种技术提出反对意见。因此,如果我们对程序的自包含资源管理和使用 C++ 风格的 finally 关键字感兴趣,请考虑以下内容。
我经常最初对使用最终的便利承诺感到高兴,但对使用它后代码变得多么复杂感到失望。复杂的回滚操作需要嵌套,而且很多时候这个过程不是线性的,需要逻辑来根据我们通过初始化完成的程度来决定如何回滚。嵌套块结构使您可以使用线性逻辑。因此,当您需要更复杂的东西时,您会认真考虑使用 goto。
我已经经历了足够多的时间来意识到,正如我提到的 OOP 风格的 finalize 一样令人开胃,真正的 try & finally 可以在 Ada 中实现,而不会出现所有令人头疼的问题,如下面的示例所示。请注意,它远非完美,但我认为这种策略的好处大于坏处。我特别喜欢这个策略的地方在于,最终确定过程变得多么明确,所有步骤都根据 Ada 的类型系统进行标记和检查,组织良好且易于管理。 C++ 风格的 try & finally 分散了这种代码,而 Ada 的 Finalize 隐藏了它,使得更难控制故障转移逻辑的更高级别。
procedure Proc is
type Init_Stages_All is (Do_Null, Do_Place, Do_Pour, Do_Drink);
subtype Init_Stages is Init_Stages_All range Do_Place .. Do_Drink;
Init_Stage : Init_Stages_All := Do_Null;
procedure Initialize_Next is
begin
Init_Stage := Init_Stages_All'Succ(Init_Stage);
case Init_Stage is
when Do_Place => ...
when Do_Pour => ...
when Do_Drink => ...
when Do_Null => null;
end case;
end Initialize_Next;
procedure Finally is
begin
for Deinit_Stage in reverse Init_Stage .. Init_Stages'Last loop
case Deinit_Stage is
when Do_Place => ...
when Do_Pour => ...
when Do_Drink => ...
end case;
end loop;
end Finally;
begin
Initialize_Next; -- Do_Place
...
Initialize_Next; -- Do_Pour
...
Initialize_Next; -- Do_Drink
...
Finally;
exception
when E : others =>
...
Finally;
raise;
end Proc;
最后一点,此策略还可以更轻松地处理终结异常。我建议在 Finally 中创建一个过程,在引发异常时调用该过程,并通过操作 Init_Stage 以及在那里插入任何其他故障转移逻辑来递归调用 finally。