【问题标题】:Is there a better way to write the following VB6 snippet?有没有更好的方法来编写以下 VB6 片段?
【发布时间】:2012-07-19 06:19:17
【问题描述】:

我在 $COMPANY 工作,我正在帮助维护 $LEGACY_APPLICATION。它是用 Visual Basic 6 编写的。

由于缺乏 VB6 在 if 语句中执行短路评估的能力(这将大大简化这一点),我面临着做一个令人不快的复杂嵌套 if 语句。我试过AndAlso,但无济于事。必须是VB6之后添加的功能。

某处某处的某位天才指出,如果您有耐心,您可以欺骗 select case 语句使其像短路 if 语句一样工作,所以我尝试了,这就是我想出的:

Select Case (True) ' pretend this is an if-else statement
    Case (item Is Nothing): Exit Sub ' we got a non-element
    Case ((item Is Not Nothing) And (lastSelected Is Nothing)): Set lastSelected = item ' we got our first good element
    Case (item = lastSelected): Exit Sub ' we already had what we got
    Case (Not item = lastSelected): Set lastSelected = item ' we got something new
End Select

这肯定有点不寻常,我不得不使用我的奇妙白板(顺便说一句,它几乎是除了计算机之外最有用的编程资源)来确保我正确映射了所有语句。

这里发生了什么事:我有一个昂贵的手术,我想尽可能避免重复。 lastSelected 是对最近传递给此计算的值的持久引用。 item 是刚刚从 GUI 接收到的参数。如果之前从未调用过该程序,lastSelected 以 Nothing 开始。 item 也可以是 Nothing。此外,如果 lastSelected 和 item 是相同的东西,则跳过计算。

如果我用 C++ 写这个,我会写:

if (item == NULL || (lastSelected != NULL && item->operator==(*lastSelected))) return;
else lastSelected = item;

但是,我不是。

问题

我怎样才能重写它以使其看起来更好并且更有意义?如果回答说“是,原因如下:X、Y、Z”或“否,原因如下:X、Y、Z”,将获得支持。

编辑

修复了 C++ 语句以匹配 VB6 语句(它们应该是等效的)

【问题讨论】:

  • 我只使用了大约一个月的 Visual Basic,但我在其他现代语言方面拥有扎实的背景。
  • VB6 和 C++ sn-p 不等价。你想把 C++ sn-p 翻译成高效的 VB6 代码吗?
  • 希望如此。如果您可以管理它,我也会满足于漂亮的代码,但效率是首要任务。

标签: vb6 styles short-circuiting select-case


【解决方案1】:

是的

我从您的案例陈述中翻译了它。我个人觉得它更容易阅读。

If Item Is Nothing Then
  Exit Sub ' we got a non-element
ElseIf LastSelected Is Nothing Then
  Set LastSelected = Item ' we got our first good element
ElseIf Item = LastSelectedItem Then
  Exit Sub ' we already had what we got
Else
  Set LastSelected = Item ' we got something new
End If

你要求解释。我尽量不用付出太多(通过重用你自己的代码 cmets)。

但无论如何它都在这里:-)

  • 首先如果没有项目,就退出。简单。
  • 否则如果LastSelected Is Nothing 那么我们知道,因为第一个if 条件失败,所以Item 存在,并且可以安全地将该值标记为Last Selected。正如你所说,我们得到了第一个好的元素。潜艇继续。
  • 但是,如果我们有 Item 和 LastSelected 的现有值,那么它们要么相等,要么不相等。如果他们相等,就退出。
  • 如果它们不相等,则更新LastSelected。正如你所说,我们有了新的东西。

【讨论】:

  • 我更想知道为什么这种方式比另一种方式更好。 (以及为什么 VB 不包括短路布尔评估)
  • 我认为它更好,因为它是用语言的术语完成的:-)。 case 语句是一种解决方法。您可以在此处进一步了解为什么 VB 不包含短路功能:stackoverflow.com/questions/1445867/…
  • @Wug,索引从 1 开始,没有短路,奇怪的打印语句等。存在是因为这是在 GW/QBasic 中完成的方式,即使在今天 VB.net 也需要特殊的 *Also 使用来短路。 If 块通常更具可读性,但 case true 的短路语义可以方便地测试然后跨引用调用; stackoverflow.com/questions/3242560/andalso-orelse-in-vba
  • +1 因为这很好,虽然我的更短,恕我直言更好。嗯,现在这有 3 个赞成票,而我的只有 2 个。呜呜!
【解决方案2】:

这更短,可读性提高了 100 倍。

编辑 Wug 将 MarkJ 原始答案中的代码编辑为:

If (item Is Nothing)
    Then Exit Sub ' we got a non-element
ElseIf (lastSelected Is Nothing) Then
    Set lastSelected = item ' we got our first go 
ElseIf (item = lastSelected) Then
    Exit Sub ' we already had what we got
End If
Set lastSelected = item ' we got something new 

这是 MarkJ 的回应。一个嵌套 if,但只有一个 Set。对我来说似乎更整洁。

If (item Is Nothing) Then 
  Exit Sub ' we got a non-element 
ElseIf Not (lastSelected Is Nothing) Then ' not our first go
  If (item = lastSelected) Then 
    Exit Sub ' we already had what we got 
  End If 
End If
Set lastSelected = item ' we got something new
' does stuff here? @Wug is that true?
  • 要比较 VB6 中的引用相等性,请使用项目 Is LastSelected。因为 item = lastSelected 可能会评估对象中的默认属性并进行比较!
  • 既然简洁似乎是一个目标,请考虑这一点。如果在条件 X 为 True 时退出 Sub,则以后无需再次检查 X。这是错误的!除非它在两次评估之间改变它的值(例如 X 是一个检查系统时钟的函数)。您正在检查项目是否是 lastSelected,然后是否不是。如果 item Is Nothing 为 False,请不要费心检查 item Is Not Nothing 是否为 True!
  • VB6不会短路backwards compatibility with ancient versions of Basic
  • 别担心 VB6 不是其他语言,放松一下!

【讨论】:

  • 我不想参考比较,因为我可能没有相同的参考。但是,引用来自用户在 GUI 中单击的某些内容,如果他们单击某些愚蠢的内容,可能最终会没有引用。
  • 或者至少它可能是。这是一个列表视图。多么做作,令人讨厌的东西。 1个索引数组。他们在想什么。
  • 我真的不喜欢它。从语言学上讲,这很丑陋,而且很容易把自己踢到脚上。也不是像 C 这样的小方法,你会导致自己出现段错误。我的意思是像从地狱本身的手榴弹吹出的弹片碎片一样,像微妙的、险恶的、O(n^3) 或更糟糕的方式粘在你的代码上。
  • 变成一个紧张的球并爆炸,wug 的碎片四处飞舞。 Anywhoo,这得到了赞成。
  • 我没有完全的编辑权限。你忘了一个(在第 4 行:If item = lastSelected) Then
【解决方案3】:

是的

我会更简单:

If item Is Nothing Then
  Exit Sub ' we got a non-element
Else
  Set lastSelected = item ' we got something to assign
End If

除非赋值lastItem有副作用(可以是赋值代码无效的属性),否则代码逻辑基本相同。

如果你不需要退出sub(sn-p在sub的末尾什么的),那么next就更简单了:

If Not (item Is Nothing) Then Set lastSelected = item

顺便说一句,你的Select Case (True) 对 VB 程序员来说看起来很奇怪 :)

【讨论】:

  • 关于这个的事情是,我跟踪前一个值的全部原因是我可以缓存操作的结果,但这并没有这样做。在比较之前我必须检查这两个值,否则我最终会出错
  • 嗯,那么 MarkJ 答案(代码)在 VB6 中是最自然的。您可能不喜欢它,但是您的类似开关的构造在 IMO 上要糟糕得多。想想可怜的 VB 编码器,谁会在明年修复这个代码 :)
  • 还有一件事 - 您不应该保存和比较参考,而是实际值(文本或 itemdata 或从中派生的东西),您的大量计算使用的。这样,您就不会遇到空值(Nothing)或指向同一对象的两个不同指针(对象引用)的问题。
  • @Arvo 谢谢夸奖。如果您喜欢我的回答,请随时为我投票 :)
  • 顺便说一句,您的 Select Case (True) 对 VB 程序员来说看起来很奇怪 :) 作为非 VB 程序员,我也觉得很奇怪。我没有对此表示赞同,因为我不能使用它:它的结果与所提出的案例不同。
【解决方案4】:

你可以像这样使用辅助函数:

Private Function pvGetItemData(oItem As ListItem) As Variant
    If Not oItem Is Nothing Then
        pvGetItemData = oItem.Tag
    Else
        pvGetItemData = -1
    End If
End Function

然后

If pvGetItemData(Item) = pvGetItemData(LastSelected) Then
    ' cache hit
Else
    ' do calc
    Set LastSelected = Item
End If

【讨论】:

  • 我想过这样做。为什么,在你看来,它更好?编辑 plawcks。
  • 更具可读性。 VB6 缺少短路If 使代码更加冗长,但仍然可以阅读。短路评估本身不可读。遵循简单的准则“永远不要将两个语句放在一行中:”或“始终在新行开始 If/Else 正文”,您可以使这段代码 sn-p 可口。
  • 这很好,但它与原始 sn-ps 的行为不同。如果 item 为 Nothing,并且 lastSelected 为 Nothing,则报告缓存命中,而原始代码将退出 Sub。我想我们不知道代码 sn-p 之后会发生什么,以及代码是否继续是否重要
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-26
  • 2013-08-18
  • 1970-01-01
相关资源
最近更新 更多