none
Crear una función Generica SPLIT que pueda escoger el tipo de retorno. RRS feed

  • Pregunta

  • Hola:

    Estoy intentando hacer una función genérica que 'sustituya' a la función SPLIT(), para que en vez de retornar un array de string, pueda 'definir' el tipo del array devuelto, ejemplo:

    Dim arrString = "1,2,3".Split(","c)
    ' arrString es un String()
    
    ' Busco crear la función SplitGen() para que haciendo:
    Dim arrInteger = "1,2,3".SplitGen(of Integer)(","c)
    ' arrInteger sea un Integer()

    Para ello he creado lo siguiente:

    <Runtime.CompilerServices.Extension()>
    Public Function SplitGen(Of T As IConvertible)(Texto As String, Separador As String) As T()
        Dim arrArray() = Texto.Split(Separador.ToCharArray)
    
        ' Me falta una función GENERICA de conversión de String a T.
        Dim genArray() As T = Array.ConvertAll(Of String, T)(arrArray, Function(x) T.parse(x))
    
        Return genArray
    End Function

    T.Parse(x) no es correcto, busco 'que poner ahí' para que haga lo que quiero, que el valor retornado sea del 'tipo T'

    Un saludo.



    • Editado LG DES viernes, 29 de julio de 2016 7:02
    viernes, 29 de julio de 2016 7:01

Respuestas

  • "LG DES" preguntó:

    > Estoy intentando hacer una función genérica que 'sustituya' a la función SPLIT(), para
    > que en vez de retornar un array de string, pueda 'definir' el tipo del array devuelto
    >
    > Para ello he creado lo siguiente:
    >
    > <Runtime.CompilerServices.Extension()>
    > Public Function SplitGen(Of T As IConvertible)(Texto As String, Separador As String) As T()
    >    Dim arrArray() = Texto.Split(Separador.ToCharArray)
    >
    >    ' Me falta una función GENERICA de conversión de String a T.
    >    Dim genArray() As T = Array.ConvertAll(Of String, T)(arrArray, Function(x) T.parse(x))
    >
    >    Return genArray
    > End Function
    >
    > T.Parse(x) no es correcto, busco 'que poner ahí' para que haga lo que quiero, que
    > el valor retornado sea del 'tipo T'

    Hola:

    Me imagino que esa función genérica la querrás para obtener una matriz de Integer, Decimal, Long, etc., con los valores numéricos existentes en la matriz de String devuelta por el método Split, para que tu función SplitGen devuelva una matriz de un tipo de dato numérico concreto en lugar de una matriz de valores alfanuméricos.

    Si eso es así, lo que yo entiendo que tienes mal es la restricción que le has indicado al tipo genérico (As IConvertible), ya que el tipo de dato del valor devuelto también tendría que implementar la interfaz IConvertible. Pero si la restricción la defines como As Structure, entonces solamente se podrá llamar con estructuras tipo Integer, Double, Single, DateTime, etc., que son aquellas que disponen de un método TryParse, devolviendo la función una matriz de valores correspondientes a la estructura especificada.

    Prueba a implementar tu función de la siguiente manera:

        <Runtime.CompilerServices.Extension()>
        Public Function SplitGen(Of T As Structure)(value As String, separador As Char) As T()
    
            Dim arrArray() = value.Split(separador)
            Dim genArray As T() = Array.ConvertAll(Of String, T)(arrArray, Function(x As String)
                                                                                                                      Dim result As T = Nothing
                                                                                                                      x.TryParse(Of T)(result)
                                                                                                                      Return result
                                                                                                            End Function)
            Return genArray
    
        End Function

    E incluye en el mismo módulo ésta otra función genérica que utilize el método TryParse con las estructuras de datos que mayormente se utilizan:

        <Extension()>
        Public Function TryParse(Of T As Structure)(value As String, ByRef result As T) As Boolean
    
            result = Nothing
            Dim flags As Boolean
    
            If (String.IsNullOrWhiteSpace(value)) Then
                Return False
            End If
    
            ' System.Type del tipo de dato genérico especificado.
            '
            Dim typeT As Type = GetType(T)
            Dim temp As Object = Nothing
    
            If (typeT.Name = "Int32") Then
                Dim n As Integer
                flags = Integer.TryParse(value, n)
                temp = n
            ElseIf (typeT.Name = "Int64") Then
                Dim n As Long
                flags = Long.TryParse(value, n)
                temp = n
            ElseIf (typeT.Name = "Decimal") Then
                Dim n As Decimal
                flags = Decimal.TryParse(value, n)
                temp = n
            ElseIf (typeT.Name = "Double") Then
                Dim n As Double
                flags = Double.TryParse(value, n)
                temp = n
            ElseIf (typeT.Name = "Single") Then
                Dim n As Single
                flags = Single.TryParse(value, n)
                temp = n
            ElseIf (typeT.Name = "Int16") Then
                Dim n As Short
                flags = Short.TryParse(value, n)
                temp = n
            ElseIf (typeT.Name = "Byte") Then
                Dim n As Byte
                flags = Byte.TryParse(value, n)
                temp = n
            ElseIf (typeT.Name = "DateTime") Then
                Dim n As DateTime
                flags = DateTime.TryParse(value, n)
                temp = n
            End If
    
            result = DirectCast(temp, T)
            Return flags
    
        End Function

    Por supuesto, le puedes añadir más tipos de datos que sean estructuras no clases, siguiendo el mismo esquema existente en los diferentes bloques ElseIf. Lo mismo hay por ahí algo que te permita convertir un valor String en cualquier tipo de dato sin tener que detectar el tipo de dato que contiene T, pero hasta que lo encuentres, lo mismo sales del paso con la función TryParse(Of T) mostrada. ;-)

    Y a tu función SplitGen la llamarías ejecutando lo siguiente:

        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    
            Dim cadena As String = "33,23,56,34,3454,24,678,12,0,37,36"
            Dim enteros As Integer() = cadena.SplitGen(Of Integer)(","c)
            For Each n As Integer In enteros
                Console.Write("{0}, ", n)
            Next
    
            Console.WriteLine()
            Console.WriteLine()
    
            cadena = "33,34|23,234|56,837|34,387|3454,22|24,280|678,3|12,9|0,7|37,76|36,928"
            Dim decimales As Decimal() = cadena.SplitGen(Of Decimal)("|"c)
            For Each n As Decimal In decimales
                Console.Write("{0}| ", n)
            Next
    
        End Sub

    Y aquí tienes el resultado de las dos matrices numéricas obtenidas en la ventana de Salida:

    Un saludo



    viernes, 29 de julio de 2016 8:46
    Moderador
  • Hola Enrique:

    Gracias por la aportación, me ha servido para 'buscar' por donde debía y he encontrado la solución:

    <Runtime.CompilerServices.Extension(), Description("SPLIT generico, convierte a array Of T.")>
    Public Function Split(Of T As Structure)(Texto As String, Separador As String) As T()
       Dim arrArray() = Texto.Split(Separador.ToCharArray)
       Return Array.ConvertAll(Of String, T)(arrArray, Function(x) Convert.ChangeType(x, GetType(T)))
    End Function


    Cambiando el Of T As Structure como me comentabas, he encontrado el Convert.ChangeType() que junto con OPTION STRICT OFF funciona, gracias.

    Un saludo

    Añadido: Poniendo esto:

    Array.ConvertAll(Of String, T)(arrArray, Function(x) CType(Convert.ChangeType(x, GetType(T)), T))

    No es necesario el Strict Off

    • Editado LG DES viernes, 29 de julio de 2016 10:57
    • Marcado como respuesta LG DES viernes, 29 de julio de 2016 10:57
    viernes, 29 de julio de 2016 10:39

Todas las respuestas

  • "LG DES" preguntó:

    > Estoy intentando hacer una función genérica que 'sustituya' a la función SPLIT(), para
    > que en vez de retornar un array de string, pueda 'definir' el tipo del array devuelto
    >
    > Para ello he creado lo siguiente:
    >
    > <Runtime.CompilerServices.Extension()>
    > Public Function SplitGen(Of T As IConvertible)(Texto As String, Separador As String) As T()
    >    Dim arrArray() = Texto.Split(Separador.ToCharArray)
    >
    >    ' Me falta una función GENERICA de conversión de String a T.
    >    Dim genArray() As T = Array.ConvertAll(Of String, T)(arrArray, Function(x) T.parse(x))
    >
    >    Return genArray
    > End Function
    >
    > T.Parse(x) no es correcto, busco 'que poner ahí' para que haga lo que quiero, que
    > el valor retornado sea del 'tipo T'

    Hola:

    Me imagino que esa función genérica la querrás para obtener una matriz de Integer, Decimal, Long, etc., con los valores numéricos existentes en la matriz de String devuelta por el método Split, para que tu función SplitGen devuelva una matriz de un tipo de dato numérico concreto en lugar de una matriz de valores alfanuméricos.

    Si eso es así, lo que yo entiendo que tienes mal es la restricción que le has indicado al tipo genérico (As IConvertible), ya que el tipo de dato del valor devuelto también tendría que implementar la interfaz IConvertible. Pero si la restricción la defines como As Structure, entonces solamente se podrá llamar con estructuras tipo Integer, Double, Single, DateTime, etc., que son aquellas que disponen de un método TryParse, devolviendo la función una matriz de valores correspondientes a la estructura especificada.

    Prueba a implementar tu función de la siguiente manera:

        <Runtime.CompilerServices.Extension()>
        Public Function SplitGen(Of T As Structure)(value As String, separador As Char) As T()
    
            Dim arrArray() = value.Split(separador)
            Dim genArray As T() = Array.ConvertAll(Of String, T)(arrArray, Function(x As String)
                                                                                                                      Dim result As T = Nothing
                                                                                                                      x.TryParse(Of T)(result)
                                                                                                                      Return result
                                                                                                            End Function)
            Return genArray
    
        End Function

    E incluye en el mismo módulo ésta otra función genérica que utilize el método TryParse con las estructuras de datos que mayormente se utilizan:

        <Extension()>
        Public Function TryParse(Of T As Structure)(value As String, ByRef result As T) As Boolean
    
            result = Nothing
            Dim flags As Boolean
    
            If (String.IsNullOrWhiteSpace(value)) Then
                Return False
            End If
    
            ' System.Type del tipo de dato genérico especificado.
            '
            Dim typeT As Type = GetType(T)
            Dim temp As Object = Nothing
    
            If (typeT.Name = "Int32") Then
                Dim n As Integer
                flags = Integer.TryParse(value, n)
                temp = n
            ElseIf (typeT.Name = "Int64") Then
                Dim n As Long
                flags = Long.TryParse(value, n)
                temp = n
            ElseIf (typeT.Name = "Decimal") Then
                Dim n As Decimal
                flags = Decimal.TryParse(value, n)
                temp = n
            ElseIf (typeT.Name = "Double") Then
                Dim n As Double
                flags = Double.TryParse(value, n)
                temp = n
            ElseIf (typeT.Name = "Single") Then
                Dim n As Single
                flags = Single.TryParse(value, n)
                temp = n
            ElseIf (typeT.Name = "Int16") Then
                Dim n As Short
                flags = Short.TryParse(value, n)
                temp = n
            ElseIf (typeT.Name = "Byte") Then
                Dim n As Byte
                flags = Byte.TryParse(value, n)
                temp = n
            ElseIf (typeT.Name = "DateTime") Then
                Dim n As DateTime
                flags = DateTime.TryParse(value, n)
                temp = n
            End If
    
            result = DirectCast(temp, T)
            Return flags
    
        End Function

    Por supuesto, le puedes añadir más tipos de datos que sean estructuras no clases, siguiendo el mismo esquema existente en los diferentes bloques ElseIf. Lo mismo hay por ahí algo que te permita convertir un valor String en cualquier tipo de dato sin tener que detectar el tipo de dato que contiene T, pero hasta que lo encuentres, lo mismo sales del paso con la función TryParse(Of T) mostrada. ;-)

    Y a tu función SplitGen la llamarías ejecutando lo siguiente:

        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    
            Dim cadena As String = "33,23,56,34,3454,24,678,12,0,37,36"
            Dim enteros As Integer() = cadena.SplitGen(Of Integer)(","c)
            For Each n As Integer In enteros
                Console.Write("{0}, ", n)
            Next
    
            Console.WriteLine()
            Console.WriteLine()
    
            cadena = "33,34|23,234|56,837|34,387|3454,22|24,280|678,3|12,9|0,7|37,76|36,928"
            Dim decimales As Decimal() = cadena.SplitGen(Of Decimal)("|"c)
            For Each n As Decimal In decimales
                Console.Write("{0}| ", n)
            Next
    
        End Sub

    Y aquí tienes el resultado de las dos matrices numéricas obtenidas en la ventana de Salida:

    Un saludo



    viernes, 29 de julio de 2016 8:46
    Moderador
  • Hola Enrique:

    Gracias por la aportación, me ha servido para 'buscar' por donde debía y he encontrado la solución:

    <Runtime.CompilerServices.Extension(), Description("SPLIT generico, convierte a array Of T.")>
    Public Function Split(Of T As Structure)(Texto As String, Separador As String) As T()
       Dim arrArray() = Texto.Split(Separador.ToCharArray)
       Return Array.ConvertAll(Of String, T)(arrArray, Function(x) Convert.ChangeType(x, GetType(T)))
    End Function


    Cambiando el Of T As Structure como me comentabas, he encontrado el Convert.ChangeType() que junto con OPTION STRICT OFF funciona, gracias.

    Un saludo

    Añadido: Poniendo esto:

    Array.ConvertAll(Of String, T)(arrArray, Function(x) CType(Convert.ChangeType(x, GetType(T)), T))

    No es necesario el Strict Off

    • Editado LG DES viernes, 29 de julio de 2016 10:57
    • Marcado como respuesta LG DES viernes, 29 de julio de 2016 10:57
    viernes, 29 de julio de 2016 10:39