【问题标题】:Returning different types from a function从函数返回不同的类型
【发布时间】:2019-06-06 08:13:11
【问题描述】:

所以我写了一个函数来检查给它的字符串是真、假还是别的什么。如果是 True,我希望它返回布尔值 true,如果它是 false,我希望返回布尔值 false,如果是其他值,我希望它继续返回字符串。

Public Function ConvertToBoolean(InputString As String) As Variant
Dim TempResults As Variant


    If InputString = "True" Then
        TempResults = True
    ElseIf InputString = "False" Then
        TempResults = False
    Else
        TempResults = InputString
    End If

    ConvertToBoolean = TempResults


End Function

但是,它一直默认为字符串。我可以将其调暗为布尔值,但随后常规字符串将返回为 TRUE,而不是字符串。

如何强制/使函数类型转换工作?可能在 if 语句中需要它,但我不知道该怎么做。

不能在 if 内部变暗(虽然也许我可以重新变暗?要去检查)

编辑:进一步的测试表明,大多数情况下都会返回一个布尔值 - 但有时它会返回一个字符串,其中包含我首先尝试修复的所有相关问题。所以我的问题还是一样:

如何显式声明一个函数(或变量)以返回一种或另一种给定类型?

编辑 2:因为人们似乎不相信代码并不总是完美或每次都一样:

【问题讨论】:

标签: excel vba


【解决方案1】:

除非我遗漏了什么:

Public Function ConvertToBoolean(InputString As String) As Variant
Dim s As String
Dim ret
    s = Trim$(UCase$(InputString))
    Select Case s
        Case "TRUE", "FALSE"
            ret = CBool(s)
        Case Else
            ret = InputString
    End Select
    ConvertToBoolean = ret
End Function

【讨论】:

  • 由于这个 Q 非常关注 Variant 和隐式类型转换,值得注意的是 TrimUCase 都接受并返回 Variant,而 Trim$UCase$ 两者获取并返回String。我会使用StrComp 而不是大写输入,但是这段代码没有理由炸毁或失败,如果输入单元格实际上包含工作表Error 值,则除了类型不匹配之外,它不能被强制转换成String - 但我认为 Excel 会处理这个问题。非常优雅,好评。
【解决方案2】:

为了未来的读者:

根本问题是区分大小写,通过确保比较是 Text 而不是 Binary 来解决

这是证据

请注意,布尔值显示为大写,居中对齐。字符串值的原始大小写,左对齐,数字右对齐。

这是一个版本,针对基于文本的测试进行了优化,可处理错误、数字和自包含(这不是以下测试中使用的版本,因为它包含附加功能)

Public Function ConvertToBoolean(Val As Variant) As Variant
    If IsError(Val) Then
        ConvertToBoolean = Val
    ElseIf IsEmpty(Val) Then
        ConvertToBoolean = vbNullString
    ElseIf StrComp(Trim$(Val), "True", vbTextCompare) = 0 Then
        ConvertToBoolean = True
    ElseIf StrComp(Trim$(Val), "False", vbTextCompare) = 0 Then
        ConvertToBoolean = False
    Else
        ConvertToBoolean = Val
    End If
End Function

测试基于此代码

Option Explicit
Option Compare Text

Public Function ConvertToBoolean1a(InputString As String) As Variant
    Dim TempResults As Variant

    If InputString = "True" Then
        TempResults = True
    ElseIf InputString = "False" Then
        TempResults = False
    Else
        TempResults = InputString
    End If

    ConvertToBoolean1a = TempResults
End Function

Public Function ConvertToBoolean2a(InputString As String) As Variant
    Dim IsBoolean As Boolean
    Dim ReturnString As String
    Dim ReturnBoolean As Boolean

    If InputString = "True" Then
        IsBoolean = True
        ReturnBoolean = True
    ElseIf InputString = "False" Then
        IsBoolean = True
        ReturnBoolean = False
    Else
        IsBoolean = False
        ReturnString = InputString
    End If

    If IsBoolean Then
        ConvertToBoolean2a = ReturnBoolean
    Else
        ConvertToBoolean2a = ReturnString
    End If
End Function

在一个单独的模块中(我稍微修改了 Davids 的版本以进行公平比较。他的原始版本处理前导/尾随空格,但这是一个额外的功能并且需要时间)

Option Explicit

Public Function ConvertToBoolean1b(InputString As String) As Variant
    Dim TempResults As Variant

    If InputString = "True" Then
        TempResults = True
    ElseIf InputString = "False" Then
        TempResults = False
    Else
        TempResults = InputString
    End If

    ConvertToBoolean1b = TempResults
End Function

Public Function ConvertToBoolean2b(InputString As String) As Variant
    Dim IsBoolean As Boolean
    Dim ReturnString As String
    Dim ReturnBoolean As Boolean

    If InputString = "True" Then
        IsBoolean = True
        ReturnBoolean = True
    ElseIf InputString = "False" Then
        IsBoolean = True
        ReturnBoolean = False
    Else
        IsBoolean = False
        ReturnString = InputString
    End If

    If IsBoolean Then
        ConvertToBoolean2b = ReturnBoolean
    Else
        ConvertToBoolean2b = ReturnString
    End If
End Function

Public Function ConvertToBoolean3(Val As String) As Variant
    If StrComp(Val, "True", vbTextCompare) = 0 Then
        ConvertToBoolean3 = True
    ElseIf StrComp(Val, "False", vbTextCompare) = 0 Then
        ConvertToBoolean3 = False
    Else
        ConvertToBoolean3 = Val
    End If
End Function


Public Function ConvertToBoolean4(Val As Variant) As Variant
    Dim s As String
    s = UCase$(Val)
    Select Case s
        Case "TRUE", "FALSE"
            ConvertToBoolean4 = CBool(s)
        Case Else
            ConvertToBoolean4 = Val
    End Select
End Function

我还使用此代码进行了速度测试以比较性能

Sub Test()
    Dim n As Long, i As Long, j As Long
    Dim T1 As Single, T2 As Single, T3 As Single, T4 As Single
    Dim res As Variant

    Dim Dat2(1 To 5) As String
    Dat2(1) = "true"
    Dat2(2) = "false"
    Dat2(3) = "   true"
    Dat2(4) = "zx"
    Dat2(5) = ""
    Dim cl As Range

    Application.Calculation = xlCalculationManual

    n = 1000000

    T1 = Timer()
    For i = 1 To n
        For j = 1 To 5
            res = ConvertToBoolean1a(Dat2(j))
        Next
    Next
    T2 = Timer()

    T3 = Timer()
    For i = 1 To n
        For j = 1 To 5
            res = ConvertToBoolean2a(Dat2(j))
        Next
    Next
    T4 = Timer()

    'Verion 1a OP 1
    [F22] = (T2 - T1) / n * 1000000#
    'Verion 2a OP 2
    [H22] = (T4 - T3) / n * 1000000#


    n = n / 100

    Set cl = [f10:f15]
    T1 = Timer()
    For i = 1 To n
        cl.Calculate
    Next
    T2 = Timer()

    Set cl = [h10:h15]
    T3 = Timer()
    For i = 1 To n
        cl.Calculate
    Next
    T4 = Timer()

    'Verion 1a OP 1 UDF
    [F23] = (T2 - T1) / n * 1000000#
    'Verion 2a OP 1 UDF
    [H23] = (T4 - T3) / n * 1000000#

End Sub

Sub Test2()
    Dim n As Long, i As Long, j As Long
    Dim T1 As Single, T2 As Single, T3 As Single, T4 As Single
    Dim res As Variant

    Dim Dat2(1 To 5) As String
    Dat2(1) = "true"
    Dat2(2) = "false"
    Dat2(3) = "   true"
    Dat2(4) = "zx"
    Dat2(5) = ""
    Dim cl As Range

    Application.Calculation = xlCalculationManual

    n = 1000000
    T1 = Timer()
    For i = 1 To n
        For j = 1 To 5
            res = ConvertToBoolean3(Dat2(j))
        Next
    Next
    T2 = Timer()

    T3 = Timer()
    For i = 1 To n
        For j = 1 To 5
            res = ConvertToBoolean4(Dat2(j))
        Next
    Next
    T4 = Timer()

    'Verion 3 mine
    [J22] = (T2 - T1) / n * 1000000#
    'Verion 4 david
    [K22] = (T4 - T3) / n * 1000000#


    n = n / 100
    Set cl = [j10:j15]
    T1 = Timer()
    For i = 1 To n
        cl.Calculate
    Next
    T2 = Timer()

    Set cl = [K10:K15]
    T3 = Timer()
    For i = 1 To n
        cl.Calculate
    Next
    T4 = Timer()

    'Verion 3 mine  UDF
    [J23] = (T2 - T1) / n * 1000000#
    'Verion 4 david UDF
    [K23] = (T4 - T3) / n * 1000000#


End Sub

【讨论】:

    【解决方案3】:

    非常感谢@MathieuGuindon 向我解释了变体是如何工作的,并为我指明了正确的道路。

    这样,我的代码现在是:

    Option Compare Text
    Public Function ConvertToBoolean(InputString As String) As Variant
    Dim IsBoolean As Boolean
    Dim ReturnString As String
    Dim ReturnBoolean As Boolean
    
    
    If InputString = "True" Then
        IsBoolean = True
        ReturnBoolean = True
    ElseIf InputString = "False" Then
        IsBoolean = True
        ReturnBoolean = False
    Else
        IsBoolean = False
        ReturnString = InputString
    End If
    
    If IsBoolean Then
        ConvertToBoolean = ReturnBoolean
    Else
        ConvertToBoolean = ReturnString
    End If
    
    
    End Function
    

    当然,正如@MathieuGuindon 所提到的,如果你想让这段代码实际工作,(而不是“强制类型”练习),你需要添加 StrComp() (首选)或 Option比较文本(不太可取,显然有问题)

    【讨论】:

    • 此修改版本(以及原始版本)当且仅当您包含Option Compare Text 时才可以使用。我知道你说你有那行,但我可以重现你的问题的唯一方法是省略它(你的代码的两个版本都一样)
    • @chrisneilsen:是的,这使代码按预期方式工作,但与我提出的问题几乎没有关系
    猜你喜欢
    • 2017-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-27
    • 1970-01-01
    相关资源
    最近更新 更多