VB.NET Generic Function

2020-06-04 03:13发布

问题:

What I want to do is, based on the type of T do different opperations. Below is a simple example of my problem.

Public Shared Function Example(Of T)() As T
    Dim retval As T
    If TypeOf retval Is String Then
        Dim myString As String = "Hello"
        retval = myString
    ElseIf TypeOf retval Is Integer Then
        Dim myInt As Integer = 101
        retval = myInt
    End If
    Return retval
End Function

I get the error "Value of Type 'String' Cannot be converted to 'T'" Same with the integer part. If I cast either to an object before asigning them to retval it works but I think that would defeat my purpose and be less efficient. Any Ideas? Thanks!

回答1:

It's probably a bit late, but try this:

    Public Shared Function CAnyType(Of T)(ByRef UTO As Object) As T
        Return CType(UTO, T)
    End Function


    Public Shared Function ExecuteSQLstmtScalar(Of T)(ByVal strSQL As String) As T
        Dim T_ReturnValue As T

        ' Here we have the result of a DB query ' 
        Dim obj As Object = "Value from DB query cmd.ExecuteScalar"
        Dim strReturnValue As Object = obj.ToString();



        Try
            Dim tReturnType As Type = GetType(T)

            If tReturnType Is GetType(String) Then
                Return CAnyType(Of T)(strReturnValue)
            ElseIf tReturnType Is GetType(Boolean) Then
                Dim bReturnValue As Boolean = Boolean.Parse(strReturnValue)
                Return CAnyType(Of T)(bReturnValue)
            ElseIf tReturnType Is GetType(Integer) Then
                Dim iReturnValue As Integer = Integer.Parse(strReturnValue)
                Return CAnyType(Of T)(iReturnValue)
            ElseIf tReturnType Is GetType(Long) Then
                Dim lngReturnValue As Long = Long.Parse(strReturnValue)
                Return CAnyType(Of T)(lngReturnValue)
            Else
                MsgBox("ExecuteSQLstmtScalar(Of T): This type is not yet defined.")
            End If

        Catch ex As Exception

        End Try

        Return Nothing
    End Function

(the secrect is casting your generic result to object, then casting from type Object to template type T).

PS:
You are responsible to ensure that your code works correctly with nullable types and NOT nullable types, as well as System.DbNull.Value. For example when string is NULL and return value type is Boolean (not nullable). On a sidenote, please also note that VB Nothing is NOT equal NULL, it's equal to C#'s default(T) (e.g. System.Guid.Empty for Guid)



回答2:

With a generic method, T will be of exactly one type each time. Let's say that you have code calling Example(Of Integer). Now, in your mind, replace T with Integer. The resulting method will contain these lines (amongst others).

Dim retval As Integer
    If TypeOf retval Is String Then
        Dim myString As String = "Hello"
        retval = myString
' more code follows '

Assigning a String to an integer like that will never work. Sure, that code will also never execute, since the If-block prevents that, but the code will still not compile. (As a side not, the above code will fail to compile because the TypeOf keyword is restricted to use with reference types, but that is another story)

Typically when creating generic methods, you will want to do the same thing with whatever input you get, but in a type safe manner. If you want to have different behavior for different types of input, you are usually better off by overloading the methods instead.



回答3:

retVal = (T) "Hello World!"


回答4:

Do retval = Ctype(Mystring, T) or retVal = Ctype(MyInt, T)



回答5:

An alternative solution is encapsulate this kind of logic in a class and use VB CallByName function:


    Class Aux(Of T)
        Public Value As T

        Private dicc As Dictionary(Of String, Object)

        Sub New()
            dicc = New Dictionary(Of String, Object)
            dicc.Add("system.string", "hola")
            dicc.Add("system.int32", 15)
            dicc.Add("system.double", 15.0)
        End Sub

        Public Function Test() As T
            Dim typeName As String = GetType(T).ToString.ToLower

            If dicc.ContainsKey(typeName) Then
                CallByName(Me, "Value", CallType.Set, dicc(typeName))
            End If

            Return Value
        End Function

        Protected Overrides Sub Finalize()
            MyBase.Finalize()

            If Not (dicc Is Nothing) Then dicc.Clear()
            dicc = Nothing
        End Sub
    End Class