1

我有一个函数,它接受多个参数,每个参数都可能是一组类型中的一个。

我可以动态地处理这个问题,如果它失败了就会抛出一个类型错误,就像下面的代码片段一样,但我更愿意在编译时捕获这些类型错误。

Function specializingFunction(a) As String
    Select Case a.GetType
        Case GetType(Integer)
            Return "INT"
        Case GetType(Boolean)
            Return "BOOL"
        Case Else
            Return "__UNKNOWN__"  ' or throw an exception
    End Select
End Function

Sub mayFail(a1, a2, a3, a4)
    Console.WriteLine(specializingFunction(a1))
    Console.WriteLine(specializingFunction(a2))
    Console.WriteLine(specializingFunction(a3))
    Console.WriteLine(specializingFunction(a4))
End Sub

我曾希望使用 dotNET 泛型解决此问题,请参阅此问题中的最终代码示例。

我很乐意使用: 1. 一个开放的解决方案 - 客户端代码可以添加进一步的类型特化,如下面的 c++ 代码 2. 一个封闭的解决方案 - 一组固定的允许类型,可以通过代数数据类型来实现(例如) Haskell 或 C++ 中的 boost::variant

...但我很想听听两者的答案。

#include <string>
#include <iostream>

using namespace std;

string specializedFunction(bool x)
{
   return string("BOOL");
}


std::string specializedFunction(int x)
{
   return string("INT");
}

template<typename T1, typename T2, typename T3, typename T4>
void correctlyResolves(T1 a1, T2 a2, T3 a3, T4 a4)
{
   cout << specializedFunction(a1) << "\n";
   cout << specializedFunction(a2) << "\n";
   cout << specializedFunction(a3) << "\n";
   cout << specializedFunction(a4) << "\n";
}


int main()
{
    correctlyResolves(1, true, 3, 4);
    return 0;
}

哈斯克尔例子

data X = XInt Int | XBool Bool

descriminator :: X -> String  
descriminator (XInt  a) = "INT: " ++ show a
descriminator (XBool a) = "BOOL: " ++ show a


lottaArgs :: X -> X -> X -> X -> IO ()
lottaArgs a b c d = do 
                     putStrLn $ descriminator a
                     putStrLn $ descriminator b
                     putStrLn $ descriminator c
                     putStrLn $ descriminator d

main = lottaArgs (XInt 1) (XBool False) (XInt 2) (XInt 3)

我尝试的解决方案是下面的解决方案,但是似乎 VB 尝试以其通用形式实例化函数(不知道类型 T1 - T4)。因此,VB 给出了“值类型 'T1' 不能转换为 'Integer'”形式的错误。

Function specializedFunction(a As Boolean) As String
    Return "BOOL"
End Function

Function specializedFunction(a As Integer) As String
    Return "INT"
End Function

Sub failsHorribly(Of T1, T2, T3, T4)(a1 As T1, a2 As T2, a3 As T3, a4 As T4)
   Console.WriteLine(specializedFunction(a1))
   Console.WriteLine(specializedFunction(a2))
   Console.WriteLine(specializedFunction(a3))
   Console.WriteLine(specializedFunction(a4))
End Sub

Sub Main()
   failsHorribly(1, True, 3, 4)
End Sub

我怎样才能最好地在 VB 中解决这种设计问题?是否有任何适当的静态验证联合或变体类型?

Object我想我可以用一个只能用一种允许的类型构造的成员创建一个自定义类型,但这似乎不是很优雅,必须有更好的方法。

编辑:解决方案

有关基于包装器对象的解决方案的实现,请参见下面的答案

4

1 回答 1

1

我已经实现了一个具有自定义类型的解决方案,它提供双向转换,以便允许我将其用于ByRef参数。

它非常冗长,但我认为冗长是 VB 的主要设计目标 :¬)

它也不是完美的,因为它仍然不会静态跟踪参数类型,因此无论它的实际类型是什么,都提供对每个可能包含的类型的转换。

我可能会看看在此之上使用泛型来帮助解决这个问题。

下面是我在这个问题中使用的玩具 Int/Bool 示例中的这种技术的实现:

Class AllowedArgs
    Private a_ As Object  ' can't declare a_ as a reference
    '
    Private Sub New()
    End Sub
    '
    Public Sub New(ByRef s As Integer)
        a_ = s
    End Sub
    '
    Public Shared Widening Operator CType(a As Integer) As AllowedArgs
        Return New AllowedArgs(a)
    End Operator
    '
    Public Shared Widening Operator CType(a As AllowedArgs) As Integer
        If TypeOf a.a_ Is Integer Then
            Return CType(a.a_, Integer)
        Else
            Return 0
        End If
    End Operator
    '
    Public Sub New(ByRef s As Boolean)
        a_ = s
    End Sub
    '
    Public Shared Widening Operator CType(a As Boolean) As AllowedArgs
        Return New AllowedArgs(a)
    End Operator
    '
    Public Shared Widening Operator CType(a As AllowedArgs) As Boolean
        If TypeOf a.a_ Is Boolean Then
            Return CType(a.a_, Boolean)
        Else
            Return False
        End If
    End Operator
    '
    Public Overrides Function toString() As String
        Select Case a_.GetType()
            Case GetType(Integer)
                Return "INT: " + a_.ToString
            Case GetType(Boolean)
                Return "BOOL: " + a_.ToString.ToUpper
            Case Else
                Return "__THIS_SHOULD_NEVEN_HAPPEN__"
        End Select
    End Function
    '
    Public Sub setFromString(a As String)
        Select Case a_.GetType()
            Case GetType(Integer)
                Try
                    a_ = Integer.Parse(a)
                Catch ex As Exception
                    a_ = 0
                End Try
            Case GetType(Boolean)
                a_ = If(a.ToUpper = Boolean.TrueString.ToUpper, True, False)
            Case Else
                Console.WriteLine("__OH_DEAR_THIS_SHOULD_NEVER_HAPPEN__")
        End Select
    End Sub
    '
End Class

Sub actuallyWorks(ByRef a1 As AllowedArgs, ByRef a2 As AllowedArgs, ByRef a3 As AllowedArgs, ByRef a4 As AllowedArgs)
    ' this works just fine:
    Console.WriteLine(a1.toString())
    Console.WriteLine(a2.toString())
    Console.WriteLine(a3.toString())
    Console.WriteLine(a4.toString())
    ' these modifications are passed back correctly
    a1.setFromString("9999")
    a2.setFromString("9999")
    a3.setFromString("9999")
    a4.setFromString("9999")
End Sub

Sub Main()
    Dim a1 As Integer = 1
    Dim a2 As Integer = 2
    Dim a3 As Boolean = True
    Dim a4 As Integer = 3
    actuallyWorks(a1, a2, a3, a4)
    Console.WriteLine("After: " + a1.ToString)
    Console.WriteLine("After: " + a2.ToString)
    Console.WriteLine("After: " + a3.ToString)
    Console.WriteLine("After: " + a4.ToString)
    Console.In.ReadLine()
End Sub
于 2013-09-04T12:21:38.737 回答