Una colección para tus nombres-Valor


Siguiendo con la biblioteca de útiles, veamos ahora de tener una clase que nos permita almacenar y manipular varias instancias de la clase NameValue de la publicación pasada.

Personalizando una lista

Esto en realidad, puede ser tan sencillo como crear una clase que herede de la genérica List. Sin embargo, para hacerla debemos definir de que clase es esa lista y, como vimos anteriormente, estamos definiendo diferentes clases, de acuerdo al tipo de dato a almacenar.

Para ello, viene  en nuestra ayuda la interfaz INameValue.

Public Class NameValueList
    Inherits List(Of INameValue)

Y ya la tenemos. Sonrisa

Sin embargo, podríamos agregar algunas funcionalidades como el agregado de un elemento, dependiendo del tipo de dato.

Esto permitiría tener un método genérico que “traduzca” tipos de datos, por ejemplo, cuando necesitamos transformar desde otros entornos (como datos venidos desde bases de datos)

Automatizando el agregado de elementos

Definamos pues, un método que cree una instancia de la clase NameValue, y le asigne el valor recibido:

Private Function CreateNameValue(
                    name As String,
                    value As Object,
                    type As Type) As INameValue

Como vemos, el valor se recibe como de tipo objeto, que admitirá cualquier valor, y en parámetro independiente, el tipo deseado.

Lo que retorna la función es la interfaz genérica INameValue.

Internamente, la función define una variable para crear el valor de retorno, y obtenemos, como cadena de caracteres, el nombre del tipo a utilizar.

        Dim retvalue As INameValue
        Dim sName As String = type.Name

Luego, seleccionamos basados en dicho nombre, para crear la instancia a retornar, utilizando la sentencia Select Case

Select Case sName
    Case "BigInt"
        retvalue =
            New NameValue(Of Int64)(name)
    Case "Binary"
        retvalue =
            New NameValue(Of Byte())(name)
    Case "Bit"
        retvalue =
            New NameValue(Of Boolean)(name)

(la lista es bastante más larga).

Una ventaja de la sentencia Select es que nos permite ejecutar una acción, al seleccionar uno de varios valores en la misma sentencia. Así, por ejemplo, para los valores de cadenas de caracteres, podemos agruparlos en un solo case. Además, en el ejemplo, vemos que no solo usamos los tipos de datos propios de .Net, sino también, otros como VarChar, NVarchar, Text, que son propios de bases de datos:

Case "NChar",
        "NText",
        "NVarChar",
        "Text",
        "VarChar",
        "Xml",
        "String"
    retvalue =
        New NameValue(Of String)(name)

Ante la ocurrencia no cubierta, mostrar el error.

Aún cuando queramos ser muy detallistas, es factible que no contemplemos todos los tipos posibles. Por ello, si nos encontramos ante esa situación, es importante informarnos de ello con una excepción específica. Cuando ninguna de las opciones de selección ocurre, (el caso diferente), procedemos a ello:

Case Else
   Debug.WriteLine(
      String.Format(
         "Case ""{0}",type.ToString))
   Debug.WriteLine(
      String.Format(
         "retvalue = New NameValue(Of {0})(name)",
         type.ToString))
   Throw New NotImplementedException(
      String.Format(
         "Missing type={0}",
         type.ToString)
      )

Pero también existen los tipos que admiten valores nulos.

Como es posible recibir de esos tipos de datos, debiéramos contemplar también esa posibilidad.

para ello, detectamos si el tipo admite nulos (es Nullable), y utilizamos un select similar, para dichos valores, dejando el ya definido, para los que no lo admiten, encapsulando esta decisión, obviamente, con una sentencia If.

        Dim isNullable As Boolean =
            type.FullName.StartsWith("System.Nullable")
        If isNullable Then
            'Get the base type Name
            Dim splitter = Split(type.FullName, "[")
            splitter = splitter(2).Split(",")
            sName = splitter(0).Replace("System.", "")
        End If
        If isNullable Then

Exponiendo la creación de nuevos elementos.

Todo este procedimiento lo hemos incluido en un miembro privado, solo visible dentro de la propia clase, para crear el nuevo elemento. Definamos pues un método visible externamente que además de crearlo, lo agregue a la lista.

    Public Sub AddElement(
                name As String,
                value As Object,
                type As Type)
        Me.Add(
            CreateNameValue(
                name,
                value,
                type)
            )
    End Sub

Obteniendo un valor por su nombre.

La clase base List solo es capaz de retornar un elemento específico por su índice pero no por un literal (lo cual, por cierto, si puede hacerlo un Dictionary).

Pero elegimos List para que fuese más liviano y además, fácilmente serializable.

Además, perfectamente podemos implementar el método que nos retorne el valor por el nombre, con el siguiente código.

    Public Function GetByName(
                        name As String) As INameValue
        Return (
            From el
            In Me
            Where
                el.Name.ToUpper =
                name.ToUpper
                ).FirstOrDefault
    End Function

Eehhh si, estoy usando LinQ. (ya voy a publicar algo específico de esto enseguida) Sonrisa

Y así quedaría el código completito

Public Class NameValueList
    Inherits List(Of INameValue)
    Public Sub AddElement(
                name As String,
                value As Object,
                type As Type)
        Me.Add(
            CreateNameValue(
                name,
                value,
                type)
            )
    End Sub
    Private Function CreateNameValue(
                        name As String,
                        value As Object,
                        type As Type) As INameValue
        Dim retvalue As INameValue
        Dim sName As String = type.Name
        Dim isNullable As Boolean =
            type.FullName.StartsWith("System.Nullable")
        If isNullable Then
            'Get the base type Name
            Dim splitter = Split(type.FullName, "[")
            splitter = splitter(2).Split(",")
            sName = splitter(0).Replace("System.", "")
        End If
        If isNullable Then
            Select Case sName
                Case "BigInt"
                    retvalue = New NameValue(Of Int64?)(name)
                Case "Bit"
                    retvalue = New NameValue(Of Boolean?)(name)
                Case "Boolean"
                    retvalue = New NameValue(Of Boolean?)(name)
                Case "Char"
                    retvalue = New NameValue(Of Char?)(name)
                Case "DateTime"
                    retvalue = New NameValue(Of DateTime?)(name)
                Case "Decimal"
                    retvalue = New NameValue(Of Decimal?)(name)
                Case "Float"
                    retvalue = New NameValue(Of Decimal?)(name)
                Case "Int"
                    retvalue = New NameValue(Of Integer?)(name)
                Case "Money"
                    retvalue = New NameValue(Of Decimal?)(name)
                Case "Real"
                    retvalue = New NameValue(Of Double?)(name)
                Case "UniqueIdentifier"
                    retvalue = New NameValue(Of Guid?)(name)
                Case "SmallDateTime"
                    retvalue = New NameValue(Of DateTime?)(name)
                Case "SmallInt"
                    retvalue = New NameValue(Of Int16?)(name)
                Case "SmallMoney"
                    retvalue = New NameValue(Of Decimal?)(name)
                Case "TinyInt"
                    retvalue = New NameValue(Of Int16?)(name)
                Case "Date", "System.DateTime"
                    retvalue = New NameValue(Of Date?)(name)
                Case "Time"
                    retvalue = New NameValue(Of DateTime?)(name)
                Case "DateTime2"
                    retvalue = New NameValue(Of DateTime?)(name)
                Case "DateTimeOffset"
                    retvalue = New NameValue(Of TimeSpan?)(name)
                Case "Int32"
                    retvalue = New NameValue(Of System.Int32?)(name)
                Case "Int16"
                    retvalue = New NameValue(Of System.Int16?)(name)
                Case "Int64"
                    retvalue = New NameValue(Of System.Int64?)(name)
                Case "Double"
                    retvalue = New NameValue(Of System.Double?)(name)
                Case Else
                    Debug.WriteLine(String.Format("Case ""{0}", type.ToString))
                    Debug.WriteLine(String.Format("retvalue = New NameValue(Of {0})(name)", type.ToString))
                    Throw New NotImplementedException(String.Format("Missing type={0}", type.ToString))
            End Select

        Else
            Select Case sName
                Case "BigInt"
                    retvalue =
                        New NameValue(Of Int64)(name)
                Case "Binary"
                    retvalue =
                        New NameValue(Of Byte())(name)
                Case "Bit"
                    retvalue =
                        New NameValue(Of Boolean)(name)
                Case "Boolean"
                    retvalue = New NameValue(Of Boolean)(name)
                Case "Char"
                    retvalue = New NameValue(Of Char)(name)
                Case "DateTime"
                    retvalue = New NameValue(Of DateTime)(name)
                Case "Decimal"
                    retvalue = New NameValue(Of Decimal)(name)
                Case "Float"
                    retvalue = New NameValue(Of Decimal)(name)
                Case "Image"
                    retvalue = New NameValue(Of Byte())(name)
                Case "Int"
                    retvalue = New NameValue(Of Integer)(name)
                Case "Money"
                    retvalue = New NameValue(Of Decimal)(name)
                Case "NChar",
                        "NText",
                        "NVarChar",
                        "Text",
                        "VarChar",
                        "Xml",
                        "String"
                    retvalue =
                        New NameValue(Of String)(name)
                Case "Real"
                    retvalue = New NameValue(Of Double)(name)
                Case "UniqueIdentifier"
                    retvalue = New NameValue(Of Guid)(name)
                Case "SmallDateTime"
                    retvalue = New NameValue(Of DateTime)(name)
                Case "SmallInt"
                    retvalue = New NameValue(Of Int16)(name)
                Case "SmallMoney"
                    retvalue = New NameValue(Of Decimal)(name)
                Case "Timestamp"
                    retvalue = New NameValue(Of Byte())(name)
                Case "TinyInt"
                    retvalue = New NameValue(Of Int16)(name)
                Case "VarBinary"
                    retvalue = New NameValue(Of Byte())(name)
                Case "Variant"
                    retvalue = New NameValue(Of Object)(name)
                Case "Udt"
                    retvalue = New NameValue(Of Object)(name)
                Case "Structured"
                    retvalue = New NameValue(Of Object)(name)
                Case "Date", "System.DateTime"
                    retvalue = New NameValue(Of Date)(name)
                Case "Time"
                    retvalue = New NameValue(Of DateTime)(name)
                Case "DateTime2"
                    retvalue = New NameValue(Of DateTime)(name)
                Case "DateTimeOffset", "TimeSpan"
                    retvalue = New NameValue(Of TimeSpan)(name)
                Case "Int32"
                    retvalue = New NameValue(Of System.Int32)(name)
                Case "Int16"
                    retvalue = New NameValue(Of System.Int16)(name)
                Case "Int64"
                    retvalue = New NameValue(Of System.Int64)(name)
                Case "Byte[]"
                    retvalue = New NameValue(Of System.Byte())(name)
                Case "Double"
                    retvalue = New NameValue(Of System.Double)(name)
                Case Else
                    Debug.WriteLine(
                        String.Format(
                            "Case ""{0}",
                            type.ToString))
                    Debug.WriteLine(
                        String.Format(
                            "retvalue = New NameValue(Of {0})(name)",
                            type.ToString))
                    Throw New NotImplementedException(
                        String.Format(
                            "Missing type={0}",
                            type.ToString)
                        )
            End Select
        End If
        retvalue.RawValue = value
        Return retvalue
    End Function
    Public Function GetByName(
                        name As String) As INameValue
        Return (
            From el
            In Me
            Where
                el.Name.ToUpper =
                name.ToUpper
                ).FirstOrDefault
    End Function
End Class
Anuncios

Un comentario en “Una colección para tus nombres-Valor”

Comentario

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s