跳转至

重写 IDL_Object 方法时的一些注意事项

原文链接: https://www.nv5geospatialsoftware.com/Learn/Blogs/Blog-Details/some-caveats-when-overriding-idl-object-methods

18637 为本文评分:

暂无评分

重写 IDL_Object 方法时的一些注意事项

匿名作者 2015年10月22日 星期四

Barrett 上周关于在哈希对象上使用 FOREACH 的帖子让我想起了一些在使用 ENVIRasterSeries 类时遇到的问题。这个类继承自 IDL_Object 并重写了 _overloadForeach() 方法,这会改变循环的行为。

让我们用一些代码来更好地解释这一点。首先,我们可以使用 ENVI 附带的数据和 BuildTimeSeries ENVITask 来创建一个栅格系列:

e = ENVI(/headless)

dataDir = FilePath('', SUBDIR=['data','time_series'], $
                   ROOT_DIR=e.ROOT_DIR)
files = File_Search(dataDir, 'AirTemp*.dat')

oTask = ENVITask('BuildTimeSeries')
oTask.INPUT_RASTER_URI = files
oTask.OUTPUT_RASTERSERIES_URI = e.GetTemporaryFilename('series')
oTask.Execute

oSeries = oTask.OUTPUT_RASTERSERIES

如果我们在栅格系列对象上使用 FOREACH 循环,逻辑上会期望循环迭代一次,且循环元素是系列本身,但实际上它会针对栅格系列中的 12 个 ENVIRaster 进行 12 次迭代:

foreach iter, oSeries do begin
  help, iter
endforeach

输出:

ITER            ENVIRASTER <249906>
ITER            ENVIRASTER <249960>
ITER            ENVIRASTER <250014>
ITER            ENVIRASTER <250068>
ITER            ENVIRASTER <250122>
ITER            ENVIRASTER <250176>
ITER            ENVIRASTER <250230>
ITER            ENVIRASTER <250284>
ITER            ENVIRASTER <250338>
ITER            ENVIRASTER <250392>
ITER            ENVIRASTER <250446>
ITER            ENVIRASTER <250500>

ENVIRasterSeries 类这样设计是为了让你可以使用更高效的 FOREACH 循环,而不是通过 FOR 循环遍历系列大小并调用 SetRaster 方法:

for i = 0, oSeries.Count-1 do begin
  oSeries.Set, i
  iter = oSeries.Raster
  help, iter
endfor

现在假设你有一个可以接收 ENVIRasterSeries 对象数组的函数,并且你想在该数组上使用 FOREACH 循环,使得循环元素是数组中的系列:

foreach series, oSeriesArray do begin
  ; 对每个 ENVIRasterSeries 对象进行操作
endforeach

如果传入的是一个标量 ENVIRasterSeries,这将会失败,因为它会导致迭代系列中的栅格,而不是单个系列。幸运的是,我们可以利用 IDL 的数组提升功能。如果我们将 oSeriesArray 变量用方括号括起来,那么就能得到我们期望的行为。当传入标量时,它会变成 1 元素数组,因此 FOREACH 循环将迭代一次。当传入一个 N 元素数组时,它会暂时提升为一个 Nx1 的二维数组,并立即塌陷回 N 元素的一维数组。请注意,这种技术并非在所有情况下都有效。例如,如果你有一个哈希列表(可能来自 JSON_Parse()),那么将列表用方括号括起来会创建一个 1 元素数组的列表,FOREACH 循环将迭代一次,并将列表作为循环元素,而不是列表中预期的哈希。但一般来说,当你不确定得到的是数组还是标量时,这个技巧是有效的。

foreach series, [oSeriesArray] do begin
  ; 对每个 ENVIRasterSeries 对象进行操作
endforeach

另一个可能引发问题的 IDL_Object 重写方法是 _overloadSize(),因为它会让 N_Elements() 看起来像是在欺骗你。List 和 Hash 类重写了这个方法,因此在它们上调用 N_Elements() 会告诉你 List 或 Hash 中有多少项,而不是你有多少个 List 或 Hash 对象。虽然列表数组听起来像是一个你永远不会遇到的概念,但哈希数组是可能的,因为 JSON_Parse(/TOARRAY) 返回的就是这个。

假设我们正在编写一个类,该类具有一个属性是哈希,并由一个成员变量来存储该哈希。SetProperty 的常见模式是在关键字变量上使用 N_Elements() 来确定是否使用该属性名称调用了该方法:

if (N_Elements(myProp) gt 0) then begin
  self.myProp = myProp
endif

这里的缺陷是,如果传入一个空哈希,N_Elements() 函数将返回 0,我们就不会用新值更新该属性。我们可以做的是调用基类实现的 _overloadSize(),它将返回哈希对象的数量,而不是哈希中的元素数量:

IDL> h = hash('foo', 1, 'bar', 2, 'baz', 3)
IDL> n_elements(h)
          3
IDL> h.IDL_Object::_overloadSize()
          1

我最喜欢的 ENVI 扩展 确定路径条件 第二部分:试验与用户错误