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)
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)
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
Un comentario en “Una colección para tus nombres-Valor”