子函数与无返回值的函数

时间:2018-10-11 16:50:09

标签: vba

子例程(pip install pandas== )的含义是什么?为什么不使用没有返回值的if &&


修改

我的意思是,为什么关键字Sub存在?我可以在不声明返回值的情况下使用Function并且具有相同的功能,不是吗?

4 个答案:

答案 0 :(得分:16)

因为它阐明了意图

Function清楚地说:“回来时我会为您准备些东西”。 期望Function返回的内容,因为这是函数要执行的操作

Sub清楚地说:“我正在做某事,您应该期望它最终会成功”。 期望Sub执行一个动作,改变某些状态,引起一些副作用。

一个名为Function的{​​{1}}和一个名为DoSomething的{​​{1}}一样令人困惑: intent 是模糊不清,该过程的本质与它的广告方式冲突。我希望Sub会成功完成 或抛出一些错误。同样,我希望GetFoo给我一个DoSomething


因为不可返回的功能没有意义。

在几种编程语言中,不能在所有代码路径中都不返回值的GetFoo(或语义类似的构造)。在VBA中为所有内容使用无返回值的功能听起来很像滥用语言,只是因为VBA不会抱怨它。正如常识所告诉我们的,不是因为我们可以,而是应该

为什么您可以在任何地方都返回Foo而不分配它时返回Function

void

VBA bool过程类似于C#public bool DoSomething() { // do stuff... // ...and don't assign the return value. // woopsie, doesn't compile. } 方法:它的不可返回性显式,这是一件好事。


因为静态代码分析工具会抱怨。

臭名昭著的VBA编译器将不在乎您是否在永远不清楚不清楚是否隐式返回值的情况下编写代码。

当您要做的意思是返回一个值时-忘记了,因为错误始终存在-怎么做 您可以确定这一个是合法的,而另一个不是 吗?如果不梳理代码并完全理解它的所有功能以及原因,您将无法分辨。如果幸运的话,您正在寻找的是小型的专门功能,这些功能显然可以做一件事,并且做得很好。否则,您需要浪费时间了解正在发生的事情,只是为了确保应该已经显而易见的事情

诸如Rubberduck(我认为该项目)之类的静态代码分析工具将标记这些功能,因为它们是隐藏在代码库中的潜在错误,等待被您咬住:

Rubberduck code inspection results

答案 1 :(得分:9)

我可以想到几个原因。

  • 它防止调用者尝试将不存在的返回值分配给某些对象:

    Sub example()
        Dim x
        x = Foo     '<-- Potential runtime error.
        x = Bar     '<-- Compile error, Expected Function or variable.
    End Sub
    
    Function Foo()
    End Function
    
    Sub Bar()
    End Sub
    
  • 在Excel中,它允许将其用作宏(假设它没有参数)。

  • 效率较低,因为它必须将返回值压入堆栈。
  • 正在阅读代码的其他人还不清楚。

    Function Foo()
        'Do some stuff
        'WTH is the return value not assigned?!
    End Function
    
  • 它(应该假设是不错的编码实践)表明它不应该有副作用。 Sub期望具有副作用。


专门关于编辑。

  

我可以在不声明返回值的情况下使用Function,并且具有相同的功能吗?

这不是正确的陈述。如果您声明这样的函数...

Function Foo()
    'Do some stuff
End Function

... 它仍然具有返回值 -它只是隐式的。上面的代码完全等同于:

Public Function Foo() As Variant
    'Do some stuff
End Function

VBA不会强迫您显式声明返回值的类型(类似于它对Dim语句不需要显式类型的方式)。那并不意味着它没有一个,而是-没有。

答案 2 :(得分:3)

有可能(不建议,也不是一种好的做法)在所有地方使用布尔函数而不是Sub,并确保一旦到达末尾,它们甚至会返回True

赞:

Public Function Main as Boolean
     'All the code here
     Main = True
End Function

这很容易用一行测试:

Debug.Print Main

然后您可以像这样使用它:

If Not SomeFunction Then IncrementLogString ("SomeFunction") 最后,您可以使用所有错误的功能检查日志。


老实说,大约5年前,我只做过一次,因为另一个开发人员坚持要这么做,而我别无选择。关于这个事实,它可能是我所见过的最大的VBA应用程序,并且运行平稳(我不是那里的主要开发人员,因此在这里没有任何信誉),我想其中没有问题。一段时间后,我习惯了它,这很有趣。但总的来说,人们会对此不满意。

答案 3 :(得分:1)

要问的真正问题不是为什么Sub存在,而是为什么Function存在!为什么?

VBA构建在VB6之上,而VB6完全构建在COM之上。不幸的是,我找不到此源,但是所有COM方法必须返回HRESULT 。这意味着所有VBA / VB6方法都是编译后的子例程!

但是,如果所有方法都是子例程,那么我的方法怎么返回一个值?好吧,让我们看一个示例ISynchronizeHandle::GetHandle

HRESULT GetHandle(
  HANDLE *ph
);

如您所见,返回值的参数实际上是由DLL C ++标头定义中的引用提供的。按照约定,此返回类型始终是最后一个参数。因此,对于IStdMarshalInfo::GetClassForHandler的定义是:

HRESULT GetClassForHandler(
  DWORD dwDestContext,
  void  *pvDestContext,
  CLSID *pClsid
);

其中返回的类作为类的CLSID返回(最后一个参数)。

Application.Evaluate()的C ++标头看起来像这样:

HRESULT Evaluate(
     char[] sToEvaluate,
     char[] *sEvaluated
);

这类似于我们实现如下子例程的情况:

Sub Evaluate(ByVal sToEvaluate as string, ByRef sEvaluated as string)

End Sub

因此,每当调用函数时,我们都需要先准备一个返回类型,然后再调用子函数。

Dim sRet as string
Evaluate("1+1",sRet)

这种糟透了...因此,微软想出了“嘿,让我们为VB引擎提供一种返回数据的方法。我们只是将现有的子行为包装在幕后,但是我们的VM将处理实际结果并返回使用者的功能”。因此,他们可能没有包装现有的子行为,而是包装了行为并创建了新的声明Function

最终Function仅作为对Sub的事后实现。这就是Sub一开始就存在的原因。


请记住,您可以创建一个自定义VOID类,然后编写:

Function someFunction() as VOID

End Function

然后调用您的函数,如:

Call someFunction()

但是不建议这样做。