Todo ello ha propiciado el cambio de estrategia que acabamos de mencionar, que tiene el claro objetivo de que, independientemente del lenguaje que utilicemos, podamos aprovechar toda la potencia que .NET Framework pone a nuestra disposición.
Al crear una propiedad de este modo, el compilador genera internamente un campo de respaldo con ámbito de clase, cuyo nombre se compone de un guión bajo y el nombre de la propiedad. Dicho campo es perfectamente accesible desde el código de la clase, aunque no es expuesto a través de IntelliSense.
Las propiedades auto implementadas sufren algunas restricciones: no pueden ser declaradas con los modificadores ReadOnly ni WriteOnly, y en el caso de que la propiedad vaya a contener un array, no podemos especificar la dimensión del mismo en la declaración, aunque sí es posible inicializarlo, como vemos en los ejemplos del listado 1.
Listado 1. Propiedades auto-implementadas
Public Class Libro
Public Property Título As String
Public Property Autor As String
Public Property Precio As Decimal
Public Property Editorial
As String = "Netalia"
' La siguiente línea produce un error de compilación:
' Public Property Distribuidores Varios(10) As String
' La siguiente línea es correcta para crear una
' propiedad que contenga un array:
Public Property Distribuidores
As String() = New String() {
"Distribuidor01", "Distribuidor02" }
Public Sub VerCamposRespaldo()
Console.WriteLine(_Título)
Console.WriteLine(_Autor)
Console.WriteLine(_Precio)
Console.WriteLine(_Editorial)
Console.ReadLine()
End Sub
End Class
Listado 2. Inicializadores de colecciones
Dim lstLibros1 As List(Of Libro) = New List(Of Libro) From {
Dim lstLibros2 As New List(Of Libro) From {
New Libro() With {
.Título = "El camino", .Autor = "Miguel Delibes",
.Precio = 18.00D, .Editorial = "Ediciones Destino"
},
New Libro() With {
.Título = "El caballero de Olmedo", .Autor = "Lope de Vega",
.Precio = 11.50D, .Editorial = "Vicens-Vives"
}
}
{"El camino", "Miguel Delibes", 18.00D, "Ediciones Destino"},
{"El caballero de Olmedo", "Lope de Vega", 11.50D, "Vicens-Vives"} }
'------------------------------------------------------
<Extension()>
Public Sub Add(ByVal lstLibros As List(Of Libro),
ByVal sTítulo As String,
ByVal sAutor As String,
ByVal dPrecio As Decimal,
ByVal sEditorial As String)
lstLibros.Add(New Libro() With { .Título = sTítulo, .Autor = sAutor,
.Precio = dPrecio, .Editorial = sEditorial })
End Sub
Pero, al inicializar colecciones como la del listado 2, ¿no sería estupendo poder pasar solamente los valores para las propiedades de cada objeto de la colección, y que ésta se encargara de instanciar los objetos? Esto es perfectamente posible creando un método de extensión con el nombre Add para la colección List(OfLibro), lo que hará posible utilizar una sintaxis de inicialización mucho más simple, como muestra el listado 3.
En el caso de que estemos desarrollando una colección propia en la que deseemos que esté disponible esta sintaxis de inicialización, es preciso implementar la interfaz IEnumerable, o al menos cumplir con el patrón IEnumerable, es decir, implementar métodos GetEnumeratory Add. Con respecto al método Add, podemos crear una sobrecarga que facilite la sintaxis de inicialización para nuestra colección, o bien un método de extensión como en el caso anterior. En el listado 4 vemos un ejemplo.
Listado 4
Dim colBiblioteca1 As New Biblioteca() From {
New Libro() With {
.Título = "El camino", .Autor = "Miguel Delibes",
.Precio = 18.00D, .Editorial = "Ediciones Destino" },
New Libro() With {
.Título = "El caballero de Olmedo", .Autor = "Lope de Vega",
.Precio = 11.50D, .Editorial = "Vicens-Vives" }
}
Dim colBiblioteca2 As New Biblioteca() From {
{"El camino", "Miguel Delibes", 18.00D, "Ediciones Destino"},
{"El caballero de Olmedo", "Lope de Vega", 11.50D, "Vicens-Vives"} }
'-----------------------------------
Public Class Biblioteca
Implements IEnumerable(Of Libro)
Private lstLibros As List(Of Libro)
Public Sub New()
Me.lstLibros = New List(Of Libro)
End Sub
Public Sub Add(ByVal oLibro As Libro)
lstLibros.Add(oLibro)
End Sub
Public Sub Add(ByVal sTítulo As String,
ByVal sAutor As String,
ByVal dPrecio As Integer
ByVal sEditorial As String)
lstLibros.Add(New Libro() With {
.Título = sTítulo, .Autor = sAutor,
.Precio = dPrecio, .Editorial = sEditorial })
End Sub
Public Function GetEnumerator()
As System.Collections.Generic.IEnumerator(Of Libro)
Implements System.Collections.Generic.IEnumerable(Of Libro).GetEnumerator
Return lstLibros.GetEnumerator()
End Function
End Class
Listado 5
Dim Lambda01 As Func(Of Integer, String) =
Function(nNumero As Integer)
Dim nNuevoNumero As Integer
Dim sResultado As String
nNuevoNumero = nNumero * 7
sResultado = "El resultado es: " & nNuevoNumero.ToString()
Return sResultado
End Function
Console.WriteLine(Lambda01(123))
Dim Lambda02 =
Function(nDiasAgregar As Double) As DateTime
Dim dtFechaActual As DateTime = DateTime.Today
Dim dtFechaNueva As DateTime =
dtFechaActual.AddDays(nDiasAgregar)
Return dtFechaNueva
End Function
Console.WriteLine(Lambda02(5).ToString("dd-MMMM-yyyy"))
' Expresión lambda de tipo Sub
Dim Lambda03 =
Sub(sNombre As String, dtFechaNacimiento As DateTime)
Dim sMensajeCompleto As String = sNombre &
" nacido en " &
dtFechaNacimiento.ToString("yyyy")
Console.WriteLine(sMensajeCompleto)
End Sub
Lambda03("Ernesto Naranjo", New DateTime(1970, 10, 18))
' Expresión lambda de tipo Sub con declaración estricta
' usando Action(Of T)
Dim Lambda04 As Action(Of String) =
Sub(sMensaje)
Console.WriteLine(sMensaje)
End Sub
Lambda04("Hola mundo!")
Listado 6
Public Class Documento
Public Property Texto As String
Public Property Autor As String
End Class
Public Class Carta
Inherits Documento
Public Property Destinatario As String
End Class
Public Class Acta
Inherits Documento
Public Property DepartamentoEmisor As String
Public Property Fecha As DateTime
End Class
'---------------------------------------
Dim ilstCartas As IList(Of Carta) = New List(Of Carta) From {
New Carta() With {.Texto = "AAA", .Autor = "Bea",
.Destinatario = "Tom"},
New Carta() With {.Texto = "BBB", .Autor = "María",
.Destinatario = "Ana"}
}
' error de ejecución
Dim ilstDocumentos As IList(Of Documento) = ilstCartas
Si el intento de asignación de un objeto del tipo IList(Of Carta)a una variable del tipo IList(Of Documento) no produjera un error en tiempo de ejecución, podríamos reasignar a uno de los elementos de IList(OfDocumento)un tipo Actae intentar seguidamente extraerlo como un tipo Carta, como vemos en el listado 7, lo que provocaría una ruptura en el sistema de seguridad de tipos de la plataforma.
Listado 7
ilstDocumentos(1) = New Acta() With {
.Texto = "cccc", .Autor = "Ignacio",
.DepartamentoEmisor = "Contabilidad",
.Fecha = DateTime.Today }
Dim oCarta As Carta = ilstCartas(1)
La versión 4 de .NET Framework
levanta en ciertos casos estas restricciones,
permitiendo la conversión implícita o varianza entre ciertos tipos de interfaces
en dos modalidades diferentes: covarianza
y contravarianza.
La covarianza permite asignar a un tipo
como IEnumerable(Of T), en el que T esté
situado en un nivel superior de la jerarquía,
un valor de tipo IEnumerable(Of T) cuyo
T sea un descendiente, sin que se produzca
error. El compilador acepta esto debido
a que dicha interfaz está definida dentro de
la plataforma como IEnumerable(Of Out
T), lo que indica que el tipo T solamente
podrá ser manipulado en operaciones “de
salida”. De esta manera, es posible escribir
el código del listado 8.
Por otra parte, la contravarianza produce, en cierto sentido, un efecto opuesto al anterior, ya que permite, por ejemplo, que en un tipo derivado T del que hemos creado una colección List(Of T), una operación/ método como Sort sea llevada a cabo por un tipo superior en la jerarquía de clases de T mediante la interfaz IComparer( Of T). Ello es posible porque la interfaz está definida dentro de la plataforma como IComparer(Of In T), lo que indica que T solamente podrá ser manipulado en operaciones “de entrada”. El listado 9 muestra un ejemplo de este caso.
Listado 8
Dim ienumCarta As IEnumerable(Of Carta) = New List(Of Carta) From {
New Carta() With {.Texto = "AAA", .Autor = "Bea", .Destinatario = "Tom"},
New Carta() With {.Texto = "BBB", .Autor = "María", .Destinatario = "Ana"}
}
Dim ienumDocumento As IEnumerable(Of Documento) = ienumCarta
Listado 9
Public Class ComparadorDocumentos
Implements IComparer(Of Documento)
Public Function Compare(ByVal x As Documento, ByVal y As Documento) As Integer
Implements System.Collections.Generic.IComparer(Of Documento).Compare
'....
End Function
End Class
'--------------------------------
Dim lstCartas As List(Of Carta) = New List(Of Carta) From {
New Carta With {.Texto = "XXX", .Autor = "Sole", .Destinatario = "Bob"},
New Carta With {.Texto = "YYY", .Autor = "Ana", .Destinatario = "Alex"},
New Carta With {.Texto = "ZZZ", .Autor = "Marta", .Destinatario = "David"}
}
Dim icompDocumentos As IComparer(Of Documento) = New ComparadorDocumentos()
lstCartas.Sort(icompDocumentos)
Para una descripción en mayor profundidad de esta nueva característica de Visual Basic, también presente en C#, recomendamos la consulta del artículo sobre este tema que publicó recientemente dotNetManía [2].