Ada 对象中的常量元素?

Constant element in Ada object?

在 Java 或 C# 中,您通常会有 class 个成员,它们是 finalreadonly - 它们被设置一次,然后再也不会被触及。它们可以为 class.

的不同实例保存不同的值

Ada中有类似的东西吗?因此,我尝试在 Ada 中创建类似的东西:

package MyPackage is

   type MyObject is limited new OtherPackage.Object with private;

....

private

   type MyObject (...) is limited new OtherPackage.Object with
      record
         M_MyField : Integer := 10;
         M_MyConstantFactory : constant Factory.Object'Class := new Factory.Object;
      end record;

end MyPackage;

这在声明 M_MyConstantFactoryconstant components are not permitted 时失败了。有没有解决的办法?一位同事建议在包的其他地方声明它,但这意味着在所有实例之间共享一个 M_MyConstantFactory,这不是我想要的。

我是否需要接受设置后可以修改值并手动防止这种情况发生?

没有。不完全是。

如果你的组件是离散类型或访问类型,你可以将其设为判别式,从而使其不可变。

with Ada.Integer_Text_IO;

procedure Immutable_Components is

   type Instance (Immutable : Positive) is null record;

   A : Instance := (Immutable => 1);

begin
   Ada.Integer_Text_IO.Put (A.Immutable);

   --  A.Immutable := 2; --  assignment to discriminant not allowed:
end Immutable_Components;

您几乎已经有了通用解决方案(对于不能作为判别式的情况)。类型是有限私有的。包的客户端只能通过 pkg 提供的操作来修改它。只要操作不修改有问题的字段,你就有了你想要的(除非我误解了,问题是如何防止你自己修改 pkg 主体中的字段)。

在我回答这个问题之前,区分 Ada 和 Java/C# 的对象建模可能会有所帮助。在 Java 中,一切都是对象,因此所有常量都必须是 final —— 在 Ada 中,事情有点不同,Ada 的对象系统("tagged types" 在 Ada 的说法中)是建立的基于两个项目:记录和类型推导。这意味着,在教授 OOP 时,我们可以逐步引入类型推导(例如 Type Degree is new Integer;),然后是记录(即封装),然后是 private 类型(即信息隐藏),最后将所有内容与标记类型统一在一起......我假设你已经意识到了所有这些。

在 Ada 中,constant 就是:一些可以读取但 [通常] 不能写入的对象。 (例如,内存映射 IO 会让事情变得有趣。)所以我们可以说:

Package Ex1 is
  Type Stub1 is private; -- Some type, with almost nothing public.
  C1 : Constant Stub1;   -- A constant of that type.
Private
  Type Stub1 is tagged record
    Data_1 : Integer;
    Data_2 : Float;
  end;

  -- And now we can tell the compiler what C1 _is_.
  C1: Constant Stub1 := (Data_1 => 3, Data_2 => 1.2);
End Ex1;

这就是我们如何为标记类型创建常量,同时隐藏其实现细节;不过,诚然,我们本可以公开所有内容并删除整个 private 部分。

现在我们了解记录[和标记类型] 的一个有趣特征,称为判别式——它们有点像常量,有点像其他语言中的通用类型。使用判别式,我们可以根据消息长度制作大小不同的消息类型:

Package Ex2 is
  Type Message(Length : Natural) is private; -- A message.
  Function Create( Text : String ) return Message;
Private
  Type Message(Length : Natural) is record
    Data : String(1..Length) := (Others => ' '); -- Defaults to space-filled string.
  end;

  Function Create( Text : String ) return Message is
  ( Data => Text, Length => Text'Length );
End Ex2;

现在,在这种情况下,当您执行类似 X : Message := Create("Steve"); 的赋值时,变量的类型 [在本例中不受约束,在这种情况下变为 Message(5)(因为 "Steve"是 5 个字符),因此尝试使用不同大小的消息字符串重新分配是行不通的。 (所以,虽然你不能说 X:= Create("Why") 可以 X:= Create("Hello") 因为这里的判别式 [Length] 是 5。) - 所以,以这种方式,判别式在某些情况下可以像常量场一样工作。

limited 关键字表示该类型没有赋值 [但有初始化],因此您 可以 使整个类型表现为常量;这与让一个组件成为 constant 不同,当然不像 TT'Class 之间的区别那么微妙(T'Class 是类型 T 和从那里派生的所有类型,其中 T 只有 那个类型。)