我有一个带有许多参数的函数,每个参数可以是一组类型中的一个。
我可以动态处理它并在失败时抛出类型错误,如下面的代码片段,但我更愿意在编译时捕获这些类型错误。
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
成员创建一个自定义类型,只能使用其中一种允许类型构建,但这似乎不是很优雅,必须有更好的方法。
请参阅下面的答案,了解基于包装器对象的解决方案的实现
答案 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