是否有VBA等效(或复制方式)传递参数为' Out'喜欢C#?

时间:2017-08-28 23:46:36

标签: vba excel-vba word-vba excel

我通常使用VBA但是已经阅读了 C#编程黄皮书中的编程技术,这显然更适用于C#。无论如何,它提到了一种使用Out关键字传递参数的技术。

我已经知道VBA支持byValbyRef并且相当确定Out没有直接的等价物。使用Out传递参数与通过Ref传递参数略有不同。

这个答案https://stackoverflow.com/a/388781/3451115似乎很好地解释了Out& Ref

  

Ref修饰符表示:

     

该值已设置   该方法可以读取和修改它。

     

Out修饰符表示:

     

值未设置,方法无法读取,直到设置为止。   该方法必须在返回之前设置它。

在我继承的代码库中,有几个地方使用接受参数byRef的方法将值分配给变量。在我看来,虽然通过byRef做了工作,但通过Out会更安全......所以(这里是问题)是否有安全的方法/在VBA中可靠地复制Out

在我的第一次迭代中(原始问题)我想象代码会有如下模式:

Sub byOutExample(byRef foo As String)
    ' Check before running code:
    ' foo must = vbNullString
    If foo <> vbNullString then Err.Raise(someError)

    ' Do Something to assign foo
    foo = someString

    ' Check before exiting:
    ' foo must <> vbNullString 
    If foo = vbNullString then Err.Raise(someError)
End Sub

其他考虑因素:值得做,是否有更好的方法,可能出现什么问题?

修改: 我在上述Ref vs Out定义的评论中注意到,传递的参数不必是null,{ {1}},nothing等可以预先分配 - 主要标准似乎是重新分配。

根据@ ThunderFrame的回答以及empty传递的参数可以预先指定(和使用)的评论,可能以下是更好的方法:

Out

在这种情况下,只要 Sub byOutExample(ByRef foo As String) Dim barTemp As String barTemp = foo ' Do Something to assign a new value to foo (via barTemp) barTemp = someString ' Must assign new variable foo = barTemp End Sub 仅出现在上面显示的2个位置中,上述代码就是复制foo传递参数的准确方法。 VBA?

2 个答案:

答案 0 :(得分:1)

您可以通过在out中传递ByRef类型参数,然后在继续之前检查它是Nothing(或值类型的默认值)来伪执行Nothing类型参数就像你在你的例子中使用String一样。

我不会强加退出条件 - 有时空字符串是一个完全有效的返回值,就像ensembl <- useMart("ensembl") ensembl <- useDataset("hsapiens_gene_ensembl",mart=ensembl) getBM(attributes=c('chromosome_name', 'start_position', 'end_position', 'strand'), filters=c('hgnc_symbol', 'ensembl_gene_id'), values=list('TTN', 'ENSG00000155657'), mart=ensembl) 引用一样。

答案 1 :(得分:1)

答案明确是“不”,你无法在VBA中复制C#out参数修饰符。来自https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/out-parameter-modifier

  

作为out参数传递的变量不必在之前初始化   在方法调用中传递。但是,需要调用方法   在方法返回之前分配一个值。

这些方面在VBA中根本不存在。 VBA中的所有变量都使用默认值初始化,即VBA中不存在单元化变量的概念,因此第1点是不可能的;如果指定的参数没有在过程中分配值,则编译器无法对象,因此也不可能出现第2点。

即使您的示例中的编码模式依赖于Do Something to assign foo也不会解析为相关数据类型的默认值(这显然与单元化不同)。例如,以下内容会错误地抛出错误:

Public Sub Main()
    Dim income As Long, costs As Long
    Dim result As Long

    income = 1000
    costs = 500
    ProcessSpend income, costs, result

End Sub

Private Sub ProcessSpend(income As Long, costs As Long, ByRef outValue As Long)
    Const TAX_RATE As Long = 2
    Dim netCosts As Long
    Dim vbDefaultValue As Long

    netCosts = costs * TAX_RATE
    outValue = income - netCosts

    If outValue = vbDefaultValue Then Err.Raise 5, , "Unassigned value"
End Sub

所以我们真的还有这样一个问题:是否有办法接近VBA中out的特征?

  1. 单位变量:我能想到的最接近的变体是VariantObject类型,默认情况下分别初始化为EmptyNothing
  2. 在过程中分配值:最简单的方法是测试分配过程的地址是否与您想要的过程地址匹配。
  3. 这一切都倾向于帮助班:

    Option Explicit
    
    Private mNumber As Long
    Private mTargetProc As LongPtr
    Private mAssignedInProc As Boolean
    Public Sub SetTargetProc(targetProc As LongPtr)
        mTargetProc = targetProc
    End Sub
    Public Sub SetNumber(currentProc As LongPtr, val As Long)
        mAssignedInProc = (currentProc = mTargetProc)
        mNumber = val
    End Sub
    Public Property Get Number() As Long
        If mAssignedInProc Then
            Number = mNumber
        Else
            Err.Raise 5, , "Unassigned value"
        End If
    End Property
    

    然后前面的例子看起来像这样:

    Public Sub Main()
        Dim income As Long, costs As Long
        Dim result As clsOut
    
        income = 1000
        costs = 500
        ProcessSpend income, costs, result
    
        Debug.Print result.Number
    End Sub
    
    Private Sub ProcessSpend(income As Long, costs As Long, outValue As clsOut)
        Const TAX_RATE As Long = 2
        Dim netCosts As Long
    
        If outValue Is Nothing Then
            Set outValue = New clsOut
        End If
    
        outValue.SetTargetProc AddressOf ProcessSpend
        netCosts = costs * TAX_RATE
        outValue.SetNumber AddressOf ProcessSpend, income - netCosts
    End Sub
    

    但是这一切都变得非常繁重......而且我觉得好像我们试图将另一种语言的语法强加到VBA上。稍微退一步out特征并开发VBA设计的语法,然后返回Variant的函数似乎是最明显的方法。您可以通过检查函数是否返回Empty变量(适合out特征的第1点和第2点)来测试是否忘记设置'out'值:

    Public Sub Main()
        Dim income As Long, costs As Long
        Dim result As Variant
    
        income = 1000
        costs = 500
        result = ProcessedSpend(income, costs)
        If IsEmpty(result) Then Err.Raise 5, , "Unassigned value"
    
    End Sub
    
    Private Function ProcessedSpend(income As Long, costs As Long) As Variant
        Const TAX_RATE As Long = 2
        Dim netCosts As Long
    
        netCosts = costs * TAX_RATE
        'Comment out the line below to throw the unassigned error
        ProcessedSpend = income - netCosts
    End Function
    

    如果您想要传递预先指定的值,那么可以将可选参数定义为函数的参数。