Union或Variant类型的静态调度

时间:2013-09-03 21:43:47

标签: .net vb.net

我有一个带有许多参数的函数,每个参数可以是一组类型中的一个。

我可以动态处理它并在失败时抛出类型错误,如下面的代码片段,但我更愿意在编译时捕获这些类型错误。

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;
}

Haskell示例

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'形式的错误不能转换为'整数'”。

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成员创建一个自定义类型,只能使用其中一种允许类型构建,但这似乎不是很优雅,必须有更好的方法。

编辑:解决方案

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

1 个答案:

答案 0 :(得分: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