跳转至

IDL 对象编程:继承(Inherits)与包含(Contains)

原文链接: https://www.nv5geospatialsoftware.com/Learn/Blogs/Blog-Details/idl-object-programming-inherits-vs-contains

14783 为本文评分:

3.0

IDL 对象编程:继承(Inherits)与包含(Contains)

匿名 2015年7月9日,星期四

上个月,我写了一篇名为《在 IDL 中重载对象》的文章。那篇文章讨论了对象如何继承一个类并成为该类的子类。当一个对象继承另一个类时,该对象被认为是其父类之一。在上个月博客中的 Fancy_List 示例中,通过调用 ISA 函数,询问 IDL 这个 Fancy_List 是否是一个列表(list)来证明了这一点。IDL 返回了 true。

然而,有时候继承并不合适。例如,我希望我的 fancy list 包含一个哈希(HASH),用于存储列表的修改历史;哈希的键将包含列表发生更改时的时间戳,哈希的值将是哈希的最新版本。

在这个例子中,让 fancy list 继承 HASH 是没有意义的。首先,fancy list 不应该具有 HASH 的属性和方法。其次,我们也不希望说我们的 fancy list 是一个哈希。最后,因为 LIST 和 HASH 都是 IDL_Object 的子类,而 IDL 不允许 IDL_Object 被多重继承,所以下面这行代码会报错:

!null = {Fancy_List, inherits List, inherits hash}

% FANCY_LIST 已被定义,且定义冲突。 % 模块 FANCY_LIST__DEFINE 中出现 1 个编译错误。

相反,fancy list 应该包含(contain) 一个哈希,而不是直接继承它。下面的对象定义创建了一个名为 "history" 的成员变量

**pro Fancy_List__Define

!null = {Fancy_List, $     inherits List, $     history: Object_New() }

end**

请注意,这只是为哈希创建了一个占位符。哈希必须在 ::Init 方法中定义。

**function fancy_list::Init, _REF_EXTRA=extra

if (~self.List::Init(_EXTRA=extra)) then return, 0

self.history = OrderedHash()

return, 1

end**

现在,正如预期的那样,调用 ISA(fl, 'HASH') 返回零。

更新成员

每次列表被修改时,哈希都需要更新。下面是一个 ::Add 方法的示例,它在每次添加值时更新历史记录。

**pro Fancy_List::Add, value, index, _REF_EXTRA=extra

if ~Isa(value, /NUMBER) then return

self.List::Add, value, index, _EXTRA=extra

self.history[timestamp()] = self[0:self.Count()-1]

end**

(操作 self[0:self.Count()-1] 会创建包含所有元素的列表的子列表,这是一种复制列表的巧妙方法)

类似的逻辑应该添加到其他修改方法中,如 ::Remove, ::Move 等。

从对象外部检索成员

现在可以使用以下调用创建带有历史记录的 fancy list:

fl = Obj_New('Fnacy_List')

那么如何访问其中的哈希呢?

哈希可以是对象的一个属性,也可以通过一个单独的方法访问。通常,使用属性就足够了,除非在将成员传递到对象外部之前需要对其执行额外的工作。

pro Fancy_List::GetProperty, MODIFICATION_HISTORY=history, _REF_EXTRA=extra

**if (Arg_Present(history)) then begin     history = self.history   endif

if (N_Elements(extra) gt 0) then begin     ; 从超类获取未在此类中定义的任何属性。     self.List::GetProperty, _EXTRA=extra   endif

end**

使用属性的一个便利之处是,可以使用"点"语法来访问:

history = fl.MODIFICATION_HISTORY

从类外部修改成员

与通过 GetProperty 访问成员类似,通常也能够通过 SetProperty 来设置成员。然而,在这个例子中,历史记录不应该包含在 SetProperty 中,因为它应该只在对象内部追加,而不允许从外部修改。

用户可能想做的一件事是偶尔清除历史记录以节省内存。这是说明应如何通过单独的方法修改成员的一个很好的例子。Fancy_List::ClearHistory 是个好名字。

**pro Fancy_List::ClearHistory

self.history.Remove, /ALL

end**

清理成员

最后,了解当包含成员的对象被销毁时,应如何处理成员变量很重要。在许多情况下,被包含的项会在其超出作用域时被自动垃圾回收,所以无需担心。

然而,编写一个处理成员清理的 ::Cleanup 方法通常是良好的实践。这确保了无论该成员在类外部的何处被使用(它有可能陷入循环引用),它都能被妥善处置。此外,可能有些情况下,你希望在对象销毁时显式清理该成员或对其执行特殊处理。

下面是 Fancy_List 的一个 Cleanup 方法示例:

**pro Fancy_List::Cleanup

Obj_Destroy, self.history

end**

下面演示了当父对象被销毁时,历史记录也被销毁的情况:

Obj_Destroy, fl print, obj_Valid(history)

IDL 打印:

0

IDL 的 HISTOGRAM 函数 数据产品级别快速入门指南