Vous êtes sur la page 1sur 37

The Useful Generic List in VB.

NET
Example Code and Explanations of the ForEach, FindAll, and Sort Methods
By Dan Mabbutt
Ads:

C# Download

Visual Basic Tools

C# Java

ASP Net Tutorial

Net Programming
Ads
C++www.daniweb.comFree Answers to Your C++ Language Questions. Register Now!
Homebrewing Equipmentwww.chicompany.netKegs, Taps, Faucets, Coils, Shanks Over 2,000 Brewing Equipment
Parts
VB6 coding toolscode-vb.com/vb6Knowledge and productivity Code library and builders
Top Related Searches
Nbsp Nbsp Nbsp Nbsp Nbsp
Performance Benefit
Wine Collection
Generic Objects
Public Category
Programming Task
Updated June 12, 2014
The concept of "generic objects" in VB.NET is introduced in the article, Generics! Cleaner Data - Faster Code!.
Generics extend the power and flexibility of VB.NET in a lot of areas, but you get a bigger performance benefit and
more programming options in the generic List object --List(Of T) -- than with any other. Check out that article to see
a performance comparison between ArrayList and the generic List(Of T) doing the same programming task.
To use List(Of T), however, you have to understand how to implement the many methods that the .NET Framework
provides. That's what this article is about. I've programmed three examples -- using ForEach, FindAll, and Sort -- to
demonstrate how the generic List class works.
As explained in the first article linked above, step 1 is to create a generic List. You can get the data in a lot of ways,
but the simplest way is to simply Add it. In this article, I'm going to write code to classify my beer and wine
collection! So here's the code to create the collection:
First, I need an object that will represent a bottle from my collection. The code for this is totally standard. Here's my
object. (In a Windows Forms application, the Form class has to be first in a file or the Visual Studio designer won't
work correctly, so put this at the end.)
Public Class Bottle
Public Brand As String
Public Name As String
Public Category As String
Public Size As Decimal

Public Sub New( _
ByVal m_Brand As String, _
ByVal m_Name As String, _
ByVal m_Category As String, _
ByVal m_Size As Decimal)
Brand = m_Brand
Name = m_Name
Category = m_Category
Size = m_Size
End Sub
End Class
To build the collection, I Add the items. I put this in the Form Load event.
Dim Cabinet As List(Of Bottle) = _
"New List(Of Bottle)
Cabinet.Add(New Bottle( _
"Castle Creek", _
"Uintah Blanc", _
"Wine", 750))
Cabinet.Add(New Bottle( _
"Zion Canyon Brewing Company", _
"Springdale Amber Ale", _
"Beer", 355))
Cabinet.Add(New Bottle( _
"Spanish Valley Vineyards", _
"Syrah", _
"Wine", 750))
Cabinet.Add(New Bottle( _
"Wasatch Beers", _
"Polygamy Porter", _
"Beer", 355))
Cabinet.Add(New Bottle( _
"Squatters Beer", _
"Provo Girl Pilsner", _
"Beer", 355))
All this was standard code in VB.NET 1.0. But note that by defining our own Bottle object, we get the benefits of
multiple types in the same collection (in this case, both String andDecimal) and efficient, type safe "late binding".
On the next page, we get to the heart of the matter ... the ForEach, FindAll, and Sortmethods.


ForEach Example
The fun starts when we use the methods. To begin with, let's implement the familiar ForEachmethod. The Microsoft
documentation includes this usage syntax definition:
Dim instance As List
Dim action As Action(Of T)

instance.ForEach(action)
Microsoft further defines action as "delegate to a method that performs an action on the object passed to it. The
elements of the current List(T) are individually passed to the Action(T) delegate"
If you need more on delegates, you might want to try the About Visual Basic article, Using Delegates in Visual Basic
.NET for Runtime Flexibility.
The first thing you need to code is the method that will be delegated. Misunderstanding this one key point is the
source of most of the confusion of VB.NET students. This function or subroutine is where all of the customized
coding for the "Of <T>" type objects is done. When you get this right, you're essentially done. In this first example,
it's really simple. An entire instance of the Bottle is passed and the subroutine selects anything needed out of it.
Coding the ForEach itself is simple too. Just fill in the address of the delegate using the AddressOfmethod.
Sub displayBottle(ByVal b As Bottle)
ResultList.Items.Add( _
b.Brand & " - " & _
b.Name & " - " & _
b.Category & " - " & _
b.Size)
End Sub
Private Sub ForEachButton_Click( ...
ResultList.Items.Clear()
ResultList.Items.Add("For Each Example")
ResultList.Items.Add("-----------------------")
Cabinet.ForEach(AddressOf displayBottle)
End Sub
FindAll Example
FindAll is a little more complicated. The Microsoft documentation for FindAll looks like this:
Dim instance As List
Dim match As Predicate(Of T)
Dim returnValue As List(Of T)

returnValue = instance.FindAll(match)
This syntax includes a new element, Predicate(Of T). According to Microsoft, this will represent the method "that
defines a set of criteria and determines whether the specified object meets those criteria." In other words, you can
create any code that will find something in the list. I coded myPredicate(Of T) to find anything in the
"Beer"Category:
Instead of calling the delegate code for each item in the list, FindAll returns an entire List(T) containing only the
matches that result from yourPredicate(Of T). It's up to your code to both define this second List(T) and do
something with it. My code just adds the items to a ListBox.
Private Sub FindAllButton_Click(ByVal sender As System.Object, ByVal e
As System.EventArgs) Handles FindAllButton.Click
ResultList.Items.Clear()
ResultList.Items.Add("FindAll Example")
ResultList.Items.Add("-----------------------")
Dim sublist As List(Of Bottle)
sublist = Cabinet.FindAll(AddressOf findBeer)
For Each r As Bottle In sublist
ResultList.Items.Add( _
r.Brand & " - " & _
r.Name & " - " & _
r.Category & " - " & _
r.Size)
Next
End Sub
Function findBeer(ByVal b As Bottle) _
As Boolean
If (b.Category = "Beer") Then
Return True
Else
Return False
End If
End Function
Sort Example
The final method this article examines is Sort. Again, Microsoft uses some terminology you might not be familiar
with. There are actually four different overloads of the Sort method:
Sort()
Sort(IComparer(T))
Sort(Comparison(T))
Sort(Int32, Int32, IComparer(T))
This lets you use sort methods defined in the .NET Framework for the list, code your own, use a system defined
comparison for the type, or sort part of the collection using a starting position and count parameter. In this example,
since I use the syntax ...
x.Name.x.Name.CompareTo(y.Name)(y.Name)
... to actually perform the sort, I'm using the third overload. I've coded another delegate to my own comparer. Since I
want to sort by my Name, I pull just that value out of each instance of the Bottle object that is passed and use
the Sort(Comparison<(Of <(T>)>)). The Sortmethod actually rearranges the original List(T). So that's what is
processed after the method is executed.
Private Sub SortButton_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles SortButton.Click
ResultList.Items.Clear()
ResultList.Items.Add("Sort Example")
ResultList.Items.Add("-----------------------")
Cabinet.Sort(AddressOf sortCabinet)
For Each r As Bottle In Cabinet
ResultList.Items.Add( _
r.Name & " - " & _
r.Brand & " - " & _
r.Category & " - " & _
r.Size)
Next
End Sub
Private Shared Function sortCabinet( _
ByVal x As Bottle, ByVal y As Bottle) As Integer
Return x.Name.CompareTo(y.Name)
End Function
These methods were selected to demonstrate the major ways that the Framework methods inList(T) are actually
coded. There's a whole raft of other methods, however. That's what makes List(T) so useful!
If you would like to experiment with generic lists yourself, Click Here to download of the completed system above to
help you get started.
7.4.3.Creating a list of Person objects using generics (VB)

<%@ Page Language="VB" AutoEventWireup="false" CodeFile="Default.aspx.vb" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>

</div>
</form>
</body>
</html>

File: Default.aspx.vb

Imports System
Imports System.Collections
Imports System.Collections.Generic
Imports Microsoft.VisualBasic

Public Class Person
Implements IComparable
Dim FirstName As String
Dim LastName As String

Public Sub New(ByVal First As String, ByVal Last As String)
FirstName = First
LastName = Last
End Sub

Public ReadOnly Property FullName() As String
Get
Return FirstName & " " & LastName
End Get
End Property

Public Function CompareTo(ByVal obj As Object) _
As Integer Implements IComparable.CompareTo

If Not TypeOf (obj) Is Person Then
Throw New ArgumentException("Object is not a Person!")
End If

Dim p2 As Person = CType(obj, Person)
Dim lastNameResult As Integer = Me.LastName.CompareTo(p2.LastName)

If lastNameResult = 0 Then
Dim firstNameResult As Integer = Me.FirstName.CompareTo(p2.FirstName)
Return firstNameResult
Else
Return lastNameResult
End If
End Function

End Class

Partial Class _Default
Inherits System.Web.UI.Page

Protected Sub Page_Load(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles Me.Load

Dim scott As New Person("A", "F")
Dim bill As New Person("B", "E")
Dim srini As New Person("C", "D")

Dim people As New List(Of Person)
people.Add(scott)
people.Add(bill)
people.Add(srini)

For Each p As Person In people
Response.Write(p.FullName & "<BR/>")
Next

For i As Integer = 0 To people.Count - 1
Response.Write(people(i).FullName & "<BR/>")
Next

End Sub
End Class



.4.6.A generic Stack class using integers (VB)

<%@ Page Language="VB" %>

<script runat="server">
Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
Dim myStack As New Stack(Of Integer)
myStack.Push(5)
myStack.Push(3)
myStack.Push(10)

Dim myArray As Array
myArray = myStack.ToArray()

Dim x As Integer = 0
For Each item As Integer In myArray
x += item
Next

Label1.Text = x.ToString()
End Sub
</script>

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label runat="server" ID="Label1"></asp:Label>
</div>
</form>
</body>
</html>


7.4.4.A generic Stack class (VB)

<%@ Page Language="VB" %>

<script runat="server">
Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
Dim myStack As New System.Collections.Generic.Stack(Of String)
myStack.Push("A")
myStack.Push("B")
myStack.Push("C")

Dim myArray As Array
myArray = myStack.ToArray()

For Each item As String In myArray
Label1.Text += item & "<br />"
Next
End Sub
</script>

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label runat="server" ID="Label1"></asp:Label>
</div>
</form>
</body>
</html>



Bind an ASP.NET Repeater to a Generic List<>
I recently got a mail from a dotnetcurry reader who wanted to know how to bind a
Repeater control to a List<>. In this article we will see how to do so.
A Repeater control is a light weight templated data-bound list. It has no built-in
selection, editing, layout or formatting capabilities. Thus if you need capabilities like
paging, you have to explicitly build it for your applications.
Let us get started. Open Visual Studio 2008 and choose File > New > Web >
ASP.NET Web Application. Type the name as RepeaterBindToList. Choose the
desired location and language (C# or Visual Basic)
Let us first create a List<>. Right click on the project > Add New Item > Choose
Class from the Templates and type the name as EmployeeList and click OK. A
dialog appears as shown below:



Click on Yes to save the file in the App_Code folder.
Add the following code to the EmployeeList class file
C#
using System.Collections.Generic;

public class EmployeeList
{
static EmployeeList()
{
emp = new List<Employee>();
emp.Add(new Employee() { EmpID = 1, DeptID = 1, EmpName
= "Jack Nolas" });
emp.Add(new Employee() { EmpID = 2, DeptID = 4, EmpName
= "Mark Pine" });
emp.Add(new Employee() { EmpID = 3, DeptID = 3, EmpName
= "Sandra Simte"});
emp.Add(new Employee() { EmpID = 4, DeptID = 4, EmpName
= "Larry Lo" });
emp.Add(new Employee() { EmpID = 5, DeptID = 3, EmpName
= "Sudhir Panj"});
emp.Add(new Employee() { EmpID = 6, DeptID = 2, EmpName
= "Kathy K" });
emp.Add(new Employee() { EmpID = 7, DeptID = 1, EmpName
= "Kaff Joe" });
emp.Add(new Employee() { EmpID = 8, DeptID = 1, EmpName
= "Su Lie" });
}

public static List<Employee> emp { get; set; }
}

public class Employee
{
public int EmpID { get; set; }
public int DeptID { get; set; }
public string EmpName { get; set; }
}

VB.NET
Imports Microsoft.VisualBasic

Public Class EmployeeList
Shared Sub New()
emp = New List(Of Employee)()
emp.Add(New Employee() With {.EmpID = 1, .DeptID = 1, .EmpName
= "Jack Nolas"})
emp.Add(New Employee() With {.EmpID = 2, .DeptID = 4, .EmpName
= "Mark Pine"})
emp.Add(New Employee() With {.EmpID = 3, .DeptID = 3, .EmpName
= "Sandra Simte"})
emp.Add(New Employee() With {.EmpID = 4, .DeptID = 4, .EmpName
= "Larry Lo"})
emp.Add(New Employee() With {.EmpID = 5, .DeptID = 3, .EmpName
= "Sudhir Panj"})
emp.Add(New Employee() With {.EmpID = 6, .DeptID = 2, .EmpName
= "Kathy K"})
emp.Add(New Employee() With {.EmpID = 7, .DeptID = 1, .EmpName
= "Kaff Joe"})
emp.Add(New Employee() With {.EmpID = 8, .DeptID = 1, .EmpName
= "Su Lie"})
End Sub

Private Shared privateemp As List(Of Employee)

Public Shared Property emp() As List(Of Employee)
Get
Return privateemp
End Get
Set(ByVal value As List(Of Employee))
privateemp = value
End Set
End Property
End Class

Public Class Employee
Private privateEmpID As Integer
Public Property EmpID() As Integer
Get
Return privateEmpID
End Get
Set(ByVal value As Integer)
privateEmpID = value
End Set
End Property
Private privateDeptID As Integer
Public Property DeptID() As Integer
Get
Return privateDeptID
End Get
Set(ByVal value As Integer)
privateDeptID = value
End Set
End Property
Private privateEmpName As String
Public Property EmpName() As String
Get
Return privateEmpName
End Get
Set(ByVal value As String)
privateEmpName = value
End Set
End Property
End Class
Note: As you can see in the code above, we have created an EmployeeList and
exposed it through a get/set property public static List<Employee> emp
{ get; set; }. An additional advantage of using the property is that you can now
also consume this List<> using a LINQDataSource, which expects the TableName
to be a property, in our case 'emp'. So LINQDataSource can also consume a List<>
if exposed via a property.
Now return to your page and add a repeater control with a header, item and footer
template as shown below
C#
<asp:Repeater ID="rptName" runat="server">
<HeaderTemplate>
<table>
<tr>
<th>EmployeeID</th>
<th>DepartmentID</th>
<th>EmployeeName</th>
</tr>
</HeaderTemplate>
<ItemTemplate>
<tr>
<td>
<%# ((Employee)Container.DataItem).EmpID %>
</td>
<td>
<%# ((Employee)Container.DataItem).DeptID %>
</td>
<td>
<%# ((Employee)Container.DataItem).EmpName %>
</td>
</tr>
</ItemTemplate>
<FooterTemplate>
</table><br />
</FooterTemplate>
</asp:Repeater>
VB.NET
<asp:Repeater ID="rptName" runat="server">
<HeaderTemplate>
<table>
<tr>
<th>EmployeeID</th>
<th>DepartmentID</th>
<th>EmployeeName</th>
</tr>
</HeaderTemplate>
<ItemTemplate>
<tr>
<td>
<%#DirectCast(Container.DataItem, Employee).EmpID%>
</td>
<td>
<%#DirectCast(Container.DataItem, Employee).DeptID%>
</td>
<td>
<%#DirectCast(Container.DataItem, Employee).EmpName%>
</td>
</tr>
</ItemTemplate>
<FooterTemplate>
</table><br />
</FooterTemplate>
</asp:Repeater>
The <ItemTemplate> defines how items in the Repeater control are displayed. Also
observe how we have cast the Container.DataItem object to the Employee type.
This step is very essential.
Now one final step is to bind the Repeater to the List<Employee> as shown below:
C#
protected void Page_Load(object sender, EventArgs e)
{
rptName.DataSource = EmployeeList.emp;
rptName.DataBind();
}
VB.NET
Protected Sub Page_Load(ByVal sender As Object, ByVal e As Event
Args)
rptName.DataSource = EmployeeList.emp
rptName.DataBind()
End Sub
Run the application and you can see your Repeater bound to the List<Employee>



I hope this article was useful and I thank you for viewing it. The entire source
code of this article can be downloaded from here.
Gi ve a +1 to thi s arti cl e i f









Las clases base para crear colecciones
personalizadas
Tal como hemos visto, en el espacio de nombres System.Collections tenemos dos
clases abstractas que podemos usar como clases base para crear nuestras propias
colecciones.
Dependiendo que queramos crear una coleccin basada en IList, por ejemplo para
acceder a los elementos mediante un ndice numrico, o bien una coleccin basada
en IDictionary, para almacenar los elementos usando el par clave/valor,
tendremos que usar la clase CollectionBase o DictionaryBase.
Estas clases ya tienen cierta funcionalidad que podremos aprovechar para no tener
que reinventar la rueda, (sa es la "gracia" de la herencia), y lo nico que
tendremos que hacer es definir nuestros propios mtodos o propiedades para que
la coleccin acte como nosotros decidamos y, lo ms importante, para que solo
acepte los tipos de datos que realmente queramos.
Por ejemplo, si queremos almacenar datos de tipo Cliente y queremos acceder a
esos datos solo por un ndice numrico, podramos basar nuestra coleccin
en CollectionBase, pero si lo que necesitamos es una coleccin que contenga, por
ejemplo, objetos de tipo Artculo, nos podra interesar crear una coleccin basada
en DictionaryBase para que de esta forma podamos acceder a cada uno de los
elementos por medio del cdigo del artculo.
A continuacin veremos el cdigo (reducido) para crear estos dos tipos de
colecciones personalizadas.

Nota:
En las definiciones de las colecciones que vamos a mostrar, no
hemos aadido ninguna funcionalidad extra, sino que hemos creado
las clases/colecciones para que tengan un funcionamiento parecido al
de las colecciones "normales". La diferencia principal con esas
colecciones "normales" es que estas dos clases/colecciones que
vamos a mostrar, solo admitirn elementos de un tipo concreto.
Esto lo hemos hecho as para que podamos comparar y comprobar la
facilidad que ahora tenemos si usamos colecciones del espacio de
nombres System.Collection.Generic.


Crear una coleccin basada en CollectionBase
A continuacin vamos a ver un ejemplo de una coleccin personalizada basada
en CollectionBase y cmo usarla. Esta coleccin almacenar elementos de un tipo
definido por nosotros: Cliente.
Primero veamos una clase Cliente muy simple, pero que implementa la
interfaz IComparable, de forma que se puedan clasificar sus elementos por el
campo Apellidos. Tambin define el mtodo ToString, ya que esta es una
recomendacin que siempre deberamos seguir, ya que muchas de las clases de
punto NET utilizan este mtodo para mostrar el contenido de los objetos.
''' <summary>
''' Clase Cliente
''' </summary>
''' <remarks>
''' Esta clase se puede clasificar por el campo Apellidos
''' </remarks>
Public Class Cliente
Implements System.IComparable
'
Public Nombre As String
Public Apellidos As String
'
Public Sub New(ByVal nombre As String, ByVal apellidos As
String)
Me.Nombre = nombre
Me.Apellidos = apellidos
End Sub
'
Public Overrides Function ToString() As String
Return Apellidos & ", " & Nombre
End Function
'
Public Function CompareTo(ByVal obj As Object) As Integer _
Implements System.IComparable.CompareTo
If TypeOf obj Is Cliente Then
Dim cli As Cliente = DirectCast(obj, Cliente)
Return String.Compare(Me.Apellidos, cli.Apellidos)
Else
Return 0
End If
End Function
End Class

En el siguiente cdigo tenemos la definicin de la clase/coleccin Clientes, que al
estar derivada de CollectionBase tendr todos los miembros definidos en esa clase
abstracta, (que solo se puede usar para crear clases derivadas); y en la que
hemos definido los mtodos ms habituales, as como una propiedad por defecto
que nos permite acceder a los elementos mediante un ndice numrico.
Como podemos comprobar, en los mtodos que hemos definido, realmente no
tenemos que hacer demasiadas cosas, ya que en el cdigo que hemos escrito en
esos nuevos miembros nos apoyamos en las colecciones internas proporcionadas
por la clase base:
List, que es una coleccin basada en IList que contiene los elementos,
e InnerList que es una coleccin de tipo ArrayList que tambin hace referencia a
la coleccin List.
En la propiedad Item, que es la propiedad predeterminada o indizador, cuando
devolvemos el valor indicado por el ndice numrico, tenemos que hacer una
conversin para que se devuelva un objeto de tipo Cliente en lugar de uno de
tipo Object que es como realmente se almacena en la coleccin.
''' <summary>
''' Coleccin de tipo Cliente basada en IList
''' </summary>
''' <remarks></remarks>
Public Class Clientes
Inherits System.Collections.CollectionBase

Public Function Add(ByVal value As Cliente) As Integer
Return List.Add(value)
End Function

Public Function Contains(ByVal value As Cliente) As Boolean
Return List.Contains(value)
End Function

Public Function IndexOf(ByVal value As Cliente) As Integer
Return List.IndexOf(value)
End Function

Public Sub Insert(ByVal index As Integer, ByVal value As
Cliente)
List.Insert(index, value)
End Sub

Default Public Property Item(ByVal index As Integer) As
Cliente
Get
Return DirectCast(List(index), Cliente)
End Get
Set(ByVal value As Cliente)
List(index) = value
End Set
End Property

Public Sub Remove(ByVal value As Cliente)
List.Remove(value)
End Sub

Public Sub Sort()
InnerList.Sort()
End Sub
End Class

Para usar esta coleccin, lo haremos como es costumbre en las colecciones de
tipo IList:
Sub Main()
Dim col As New Clientes

col.Add(New Cliente("Pepe", "Lpez"))
col.Add(New Cliente("Loli", "Prez"))
col.Add(New Cliente("Eva", "Kelo"))
col.Add(New Cliente("Juan", "Salvador"))
col.Add(New Cliente("Miguel", "Andrade"))

col.Sort()

For i As Integer = 0 To col.Count - 1
Console.WriteLine(col(i).Apellidos)
Next

col.RemoveAt(2)

For Each cli As Cliente In col
Console.WriteLine(cli.ToString)
Next
End Sub


Crear una coleccin basada en DictionaryBase
En el siguiente cdigo veremos cmo definir una coleccin personalizada basada
en la clase abstracta DictionaryBase. Esta coleccin almacenar objetos del
tipo Artculo. Esos objetos se almacenarn indicando como clave el cdigo del
artculo.
Veamos el cdigo y comentaremos las cosas dignas de resaltar.
La clase Articulo no tiene nada que resaltar, es una clase "normalita".
''' <summary>
''' Clase artculo
''' </summary>
''' <remarks>
''' Clase para el ejemplo de coleccin derivada de DictionaryBase
''' </remarks>
Public Class Articulo
Public Codigo As String
Public Descripcion As String
Public PVP As Decimal

Sub New( ByVal codigo As String, _
ByVal descripcion As String, _
ByVal precio As Decimal)
Me.Codigo = codigo
Me.Descripcion = descripcion
Me.PVP = precio
End Sub

Public Overrides Function ToString() As String
Return Codigo & ", " & Descripcion
End Function
End Class

La clase/coleccin la derivamos de DictionaryBase para que tenga todas las
"caractersticas" expuestas por esa clase abstracta, a la que le aadimos nuevos
mtodos y propiedades para darle funcionalidad. Tal como hicimos en la
coleccin Clientes, nos apoyamos en las colecciones internas de la clase base
para realizar el trabajo de esos nuevos miembros.
''' <summary>
''' Coleccin Clientes basada en IDictionary
''' </summary>
''' <remarks>
''' </remarks>
Public Class Articulos
Inherits System.Collections.DictionaryBase

Default Public Property Item(ByVal key As String) As Articulo
Get
Return DirectCast(Dictionary(key), Articulo)
End Get
Set(ByVal value As Articulo)
Dictionary(key) = value
End Set
End Property

Public ReadOnly Property Keys() As ICollection
Get
Return Dictionary.Keys
End Get
End Property

Public ReadOnly Property Values() As ICollection
Get
Return Dictionary.Values
End Get
End Property

Public Sub Add(ByVal key As String, ByVal value As Articulo)
Dictionary.Add(key, value)
End Sub

Public Function Contains(ByVal key As String) As Boolean
Return Dictionary.Contains(key)
End Function

Public Sub Remove(ByVal key As String)
Dictionary.Remove(key)
End Sub
End Class

La forma de usar esta coleccin es la misma que cualquier coleccin basada
en IDictionary.
Dim col As New Articulos

col.Add("uno", New Articulo("Uno", "Art. Uno", 10.6D))
col.Add("dos", New Articulo("Dos", "Art. Dos", 22))
col.Add("tres", New Articulo("Tres", "Art. Tres", 45.55D))

For Each de As DictionaryEntry In col
Dim art As Articulo
art = DirectCast(de.Value, Articulo)
Console.WriteLine("{0}, {1}, {2}", de.Key, art.Descripcion,
art.PVP)
Next

col.Remove("dos")

For Each s As String In col.Keys
Console.WriteLine("{0}, {1}", s, col(s).Descripcion)
Next


Crear colecciones personalizadas usando colecciones generic
Hasta esta versin de Visual Basic, si queramos crear colecciones "fuertemente
tipadas", es decir, colecciones que solo admitieran datos del tipo que nosotros
quisiramos, tenamos que hacerlo con un cdigo parecido al que hemos visto.
Pero si nuestra intencin es crear colecciones que "simplemente" contengan
elementos de un tipo determinado, por ejemplo objetos de
tipo Cliente o Articulo, pero que no tengan ninguna funcionalidad extra a las que
de forma predeterminada tienen las clases base para crear colecciones, no es
necesario que creemos nuestros propias clases/coleccin, ya que Visual Basic 2005
puede crear colecciones con esas caractersticas sin necesidad de crear un clase
especfica.
Veamos primero el cdigo equivalente usando colecciones del espacio de
nombres Generic con respecto a los dos tipos de colecciones anteriores, y despus
explicaremos un poco de que va todo esto de los generics.


La coleccin Clientes en versin generic
La coleccin Clientes es una coleccin que solo acepta elementos del
tipo Cliente y a la que podemos acceder mediante un ndice numrico, por tanto
debemos buscar una coleccin del espacio de
nombres System.Collections.Generic que nos ofrezca esa misma funcionalidad, y
esa coleccin es: List.
Debido a que las colecciones generic necesitan saber el tipo de datos que van a
almacenar, no necesitamos crear una clase/coleccin para almacenar los
elementos del tipo Cliente, simplemente tendremos que indicar en el constructor
de esa coleccin que tipo debe contener.
En el siguiente cdigo tenemos la forma de declarar la coleccin de tipo List y
cmo acceder a los elementos que tienen, como podr comprobar es
prcticamente el mismo que el mostrado en el ejemplo de la coleccin basada
en CollectionBase.
' Coleccin generic equivalente a ArrayList
Console.WriteLine("Ejemplo usando Generic.List")
Dim colCli As New System.Collections.Generic.List(Of Cliente)

colCli.Add(New Cliente("Pepe", "Lpez"))
colCli.Add(New Cliente("Loli", "Prez"))
colCli.Add(New Cliente("Eva", "Kelo"))
colCli.Add(New Cliente("Juan", "Salvador"))
colCli.Add(New Cliente("Miguel", "Andrade"))

colCli.Sort()

For i As Integer = 0 To colCli.Count - 1
Console.WriteLine(colCli(i).Apellidos)
Next

Console.WriteLine()

' Elimina el elemento de la posicin 3
' (pero despus de haberlo clasificado)
colCli.RemoveAt(2)

For Each cli As Cliente In colCli
Console.WriteLine(cli.ToString)
Next
El "quid" de la cuestin est en la forma de declarar la variable colCli, en la que le
indicamos que el tipo de datos que contendr la coleccin es "de" Cliente:
Dim colCli As New List(Of Cliente)
Por lo dems, el cdigo a usar para acceder a los elementos, eliminarlos, etc., es
el mismo que con cualquier otra coleccin basada en IList, pero con el "detalle" de
que dicha coleccin "sepa" manejar elementos del tipo Cliente.
Si no fuera as, esta lnea producira un error, ya que estamos accediendo a un
tipo de datos que define una propiedad llamadaApellidos:
Console.WriteLine(colCli(i).Apellidos)


La coleccin Articulos en versin generic
Como vimos, la coleccin Articulos solo acepta elementos del tipo Articulo, pero
como es una coleccin de tipo IDictionary cada vez que aadimos algn elemento
o queremos acceder a cualquiera de los contenidos en ella, debemos indicar
tambin una clave. Por tanto necesitamos una coleccin generic que tenga esas
mismas "caractersticas" y la que nos puede servir es la
claseSystem.Collections.Generic.Dictionary.
A continuacin tenemos el cdigo para manejar objetos de tipo Articulo en una
coleccin tipo IDictionary, pero como veremos, en este caso hay que usar otra de
las clases del espacio de nombres Generic que nos sea til para hacer el bucle For
Each, ya que para usar la clase Generic.Dictionary debemos indicar tanto el tipo
del valor a almacenar como el de la clave.
Veamos el cdigo y despus entraremos en ms detalles:
' Coleccin generic equivalente a Hashtable (IDictionary)
Console.WriteLine("Ejemplo usando Generic.Dictionary")

Dim colArt As New System.Collections.Generic.Dictionary(Of
String, Articulo)

colArt.Add("uno", New Articulo("Uno", "Art. Uno", 10.6D))
colArt.Add("dos", New Articulo("Dos", "Art. Dos", 22))
colArt.Add("tres", New Articulo("Tres", "Art. Tres", 45.55D))

For Each de As KeyValuePair(Of String, Articulo) In colArt
Dim art As Articulo = de.Value
Console.WriteLine("{0}, {1}, {2}", de.Key, art.Descripcion,
art.PVP)
Next

Console.WriteLine()
colArt.Remove("dos")

For Each s As String In colArt.Keys
Console.WriteLine("{0}, {1}", s, colArt(s).Descripcion)
Next
Nuevamente "el truco" est en la forma de declarar la variable colArt, en la que le
decimos que la coleccin Dictionary usar claves de tipo String y valores del
tipo Articulo:
Dim colArt As New Dictionary(Of String, Articulo)
Para acceder a los elementos de esta coleccin por medio de un bucle For Each, en
lugar de usar una variable de tipoDictionaryEntry debemos usar una del
tipo generic KeyValuePair en la que debemos especificar los tipos de datos que
contiene la coleccin:
For Each de As KeyValuePair(Of String, Articulo) In colArt
Podemos comprobar que los tipos de datos que esta coleccin contiene son de
tipo String para las claves y de tipo Articulo para los valores. Dentro del bucle
hacemos una asignacin a la variable art, pero en este caso, a diferencia de
cuando usamos la coleccin "Articulos" basada en DictionaryBase, no es
necesario hacer una conversin explcita del tipo que tiene de.Value a un
tipo Articulo, ya que de.Value es del tipo Articulo.
Esto mismo lo podemos comprobar al acceder a un elemento dentro del segundo
bucle, esos son los tipos de datos que deben tener, ya que de lo contrario, no
podramos acceder a la propiedad Descripcion del objeto almacenado:
Console.WriteLine("{0}, {1}", s, colArt(s).Descripcion)


Extendiendo la clase List(Of T)
Hoy necesit exportar el contenido de una Lista a un DataTable, entonces
pens:

1. Crear una tabla con la estructura necesaria

2. Recorrer la lista secuencialmente y agregar filas a la tabla


Pero pensando como programador, se me ocurri que seguramente volviera a
necesitar esta funcin y posiblemente la necesitara con diferentes tipos de listas.

Como era lgico, pens en Generics, entonces me cre una funcin que recibe
unaSystem.Collections.Generic.List(Of T) y devuelve
un System.Data.DataTable con la siguiente firma:

Public Function ListToDataTable(Of T)(List As
System.Collections.Generic.List(Of T)) As System.Data.DataTable

despus cre toda la lgica utilizando System.Reflection para examinar las
propiedades, y aprovechando los CustomAttributes para poder ocultar
propiedades, entonces para hacer un Test, hice una clase con la siguiente
estructura:


Public Class MiClase

Private mId As Integer
Private mNombre As String
Private mCantidad As Integer

<System.ComponentModel.Browsable(False)> _
Public Property Id() As Integer
Get
Return mId
End Get
Set(ByVal value As Integer)
mId = value
End Set
End Property

Public Property Nombre() As String
Get
Return mNombre
End Get
Set(ByVal value As String)
mNombre = value
End Set
End Property

Public Property Cantidad() As Integer
Get
Return mCantidad
End Get
Set(ByVal value As Integer)
mCantidad = value
End Set
End Property

Public Sub New()

End Sub

Public Sub New(ByVal Id As Integer, ByVal Nombre As String, ByVal
Cantidad As Integer)
mId = Id
mNombre = Nombre
mCantidad = Cantidad
End Sub

End Class


como ven, la propiedad Id tiene un CustomAttribute,
elSystem.ComponentModel.BrowsableAttribute seteado con el valor False, o
sea que esa propiedad estara excluida de la exportacin.

Todo muy lindo, funcion a la perfeccin... pero se me ocurri algo mejor...
usarExtension Methods o mtodos de extensin, esa nueva posibilidad que nos
brinda el .Net Framework 3.5 de extender clases sin la necesidad de tener el cdigo
fuente de la misma.



Entonces cre un mdulo llamado ListExtension y le asign el mismo Namespace de
la lista, System.Collections.Generic.


Imports System.Reflection
Imports System.ComponentModel
Imports System.Runtime.CompilerServices

Namespace System.Collections.Generic
Module ListExtension
''' <summary>
''' Gets a Datatable with all Browsable properties of T as
columns containing all Items in the List.
''' </summary>
''' <typeparam name="T"></typeparam>
''' <param name="List">System.Collections.Generic.List(Of
T)</param>
''' <returns>System.Data.DataTable</returns>
''' <remarks>http://aprendiendonet.blogspot.com</remarks>
<Extension()> _
Public Function ToDataTable(Of T)(ByVal List As List(Of T)) As
DataTable
Dim dt As New DataTable()

Dim tipo As Type = GetType(T)
Dim members As MemberInfo() = tipo.GetMembers() '
Obtenemos todos los Miembros de la clase correspondiente al tipo T

For Each m As MemberInfo In members
If m.MemberType = MemberTypes.Property Then ' Slo nos
interesan las propiedades
Dim skip As Boolean = False

Dim atts As Object() =
m.GetCustomAttributes(GetType(BrowsableAttribute), False) ' Chequeamos
si tiene BrowsableAttribute
If atts.Length > 0 Then
If CType(atts(0),
BrowsableAttribute).Browsable = False Then
skip = True ' Seteamos un flag para no
agregar la columna
End If
End If

If Not skip Then
Dim c As DataColumn = Nothing
Try
c = New DataColumn(m.Name, CType(m,
PropertyInfo).PropertyType) ' Nueva columna con el nombre de la
propiedad y el tipo de la misma
Catch ex As Exception
c = New DataColumn(m.Name,
GetType(String)) ' En caso de error intento crearla como String
End Try
dt.Columns.Add(c)
End If
End If
Next

For Each itm As T In List ' Recorro la lista y agrego una
fila por cada item de la misma
Dim r As DataRow = dt.NewRow()
For Each c As DataColumn In r.Table.Columns
Dim aux As MemberInfo() =
tipo.GetMember(c.ColumnName)
If aux.Length > 0 Then
r(c.ColumnName) = CType(aux(0),
PropertyInfo).GetValue(itm, Nothing)
End If
Next
dt.Rows.Add(r)
Next

Return dt
End Function

End Module
End Namespace


Entonces ahora en toda mi solucin tengo la posibilidad de exportar mis Listas a
DataTable con tan solo llamar a este mtodo de extensin:

Dim auxDT as DataTable = miLista.ToDataTable()

Etiquetas: DataTable, Exportar, Extension Methods, Generics, List(Of T)
1 comentario:
1.
Mithosenero 25, 2012 3:51 p. m.
Gracias por compartir, muy util
Responder
Entrada ms recienteEntrada antiguaPgina principal
Suscribirse a: Enviar comentarios (Atom)
Buscar

Buscar

Etiquetas
.net framework

Aplicaciones de Consola
ASP.NET

Ayuda

C#

Calendar
Cast

Clipboard

Componentes
Confirmacin

Control

Cursos online

DataTable

Delegates
Dialogos

Ejemplos

Enlaces
ErrorProvider

EventLog

Eventos
Exportar

Extension Methods

Generics
IDE

IIS

Imprimir

Invoke
LinkLabel

LINQ

List(Of T)

Logs
Microsoft

Microsoft.Win32

Miembros de

My.Computer

NumericUpDown
Opciones

Path

Patrones

PDF
Portapapeles

Process

Propiedades
Recursividad

Registro de Windows
Servicios de Windows

Shortcuts

SQL Server Management Studio
System.Diagnostics

System.IO
Threads

Tipos Annimos

Tips
Trace

UI

Utilidades
VB.Net

Visual Studio
WebService

WinForms
Archivo del blog
2013 (1)
2012 (2)
2010 (2)
2009 (12)
o octubre (1)
o junio (1)
o mayo (1)
Extendiendo la clase List(Of T)
o abril (2)
o marzo (1)
o febrero (3)
o enero (3)
2008 (11)

MSDN: Visual Basic
Suscriptores de MSDN: Obtenga ahora Visual Studio 2008 RTM
Ya est disponible Visual Basic 2008 Express Edition
El tiempo de ejecucin de Microsoft .NET Framework 3.5 ya est disponible
Instintos bsicos: inferencia de tipos en Visual Basic 2008
Instintos bsicos: expresiones lambda
MSDN: Visual C# Headlines
C# news feed
Walkthrough: Writing an Async Program
Justification for Named and Optional Parameters
Top 10 Best Practice Links for using C# with Windows 8
An Introduction to New Features in C# 5.0
Lo ms visto

Como usar ErrorProviders para informar al usuario
Trabajando con Archivos y Carpetas: System.IO.Path

Como leer y escribir en el Registro de Windows desde una aplicacin .NET

SQL Server Error 4064 - La solucin

Inicializar un thread, capturar eventos y usar Invoke (VB.NET)



Vamos a hacer una lista de la tpica clase Person:

Person.cs
public class Person
{
private string _nombre;
/// <summary>
/// Nombre de la persona
/// </summary>
public string Nombre
{
get { return _nombre; }
set { _nombre = value; }
}

private string _apellido;
/// <summary>
/// Apellido de la persona
/// </summary>
public string Apellido
{
get { return __apellido; }
set { __apellido= value; }
}

private DateTime _nacimiento;
/// <summary>
/// Cuando naci la persona
/// </summary>
public DateTime Nacimiento
{
get { return _nacimiento; }
set { _nacimiento = value; }
}

public Person(string nombre, string apellido, DateTime nacimiento)
{
this.Nombre = nombre;
this.Apellido = apellido;
this.Nacimiento = nacimiento;
}
}

Listado.cs
List<Person> personas = new List<Person>();
personas.Add(new Person("Jos", "Garca", new DateTime(1940, 12, 2)));
personas.Add(new Person("Pedro", "Lpez", new DateTime(1992, 2, 22)));
personas.Add(new Person("Antonio", "Prez", new DateTime(1976, 6, 21)));

La lista es una especie de array que se va redimensionando conforme a las necesidades. Al crear la
variable List<> se inicializa su capacity, que se aumenta conforme la lista va creciendo. Pero, a no
ser que indiquemos lo contrario, es totalmente transparente a nosotros.

La cuestin es que nosotros podemos hascer la lista tan grande como queramos, y listarlo
fcilmente. Por ejemplo, vamos a ver dos modos de mostrar los elementos de la lista, colocando el
resultado en unStringBuilder:

Listado.cs
private void Mostrar1(List<Person> personas)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
foreach (Person persona in personas)
{
sb.AppendLine(persona.Nombre + " " + persona.Apellido + "<br />");
}
}

Listado.cs
private void Mostrar2(List<Person> personas)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
for (int i = 0; i < personas.Count; i++)
{
sb.AppendLine(personas[i].Nombre + " " + personas[i].Apellido + "<br />");
}
}

A m personalmente me gusta ms el modo 1

Pero obviamente, no es mostrar lo nico que se puede hacer con una lista genrica. Por ejemplo
podemos buscar los elementos de la lista que cumplan las condiciones que le indiquemos. Por
ejemplo, queremos encontrar las personas que hayan nacido a partir de 1975 (a las que
llamaremos jvenes ):

Listado.cs
private List<Person> Jovenes(List<Person> personas)
{
// Encuentra todas las personas que "jvenes"
return personas.FindAll(encuentra);

// Encuentra la primera persona joven de la lista
// personas.Find(encuentra);

// Encuentra el ndice de la primera persona joven de la lista
// personas.FindIndex(encuentra);

// Encuentra la ltima persona joven de la lista
// personas.FindLast(encuentra);

// Encuentra el ndice de la ltima persona joven de la lista
// personas.FindLastIndex(encuentra);
}

private bool encuentra(Person P)
{
return P.Nacimiento.Year > 1975;
}


Y del mismo modo podemos realizar varias operaciones interesantes, como insertar en cierto
ndice (insert), borrar una entrada (remove), vaciar la lista (clear), ordenarla (sort), comprobar si
existe cierte entrada (exist)...

Listado.cs
private void Varios(List<Person> personas)
{
// Inserta una nueva persona en el ndice 2 (tercera posicin)
personas.Insert(2, new Person("Javier", "Navarro", new DateTime(1981, 12, 21)));

// Ordena el listado de personas segn su edad
personas.Sort(ordenarPorEdad);

// Devuelve si existen jvenes en el listado. En este caso devolvera true
bool existenJovenes = personas.Exists(encuentra);

// Borrar la Persona que est en la tercera posicin (ndice 2)
personas.RemoveAt(2);

// Borra todas las personas jvenes
personas.RemoveAll(encuentra);

// Ahora devolvera false
bool existenJovenes = personas.Exists(encuentra);

// Vaca la lista
personas.Clear();
}

private bool encuentra(Person P)
{
return P.Nacimiento.Year > 1975;
}

private int ordenarPorEdad(Person P1, Person P2)
{
return P1.Nacimiento.CompareTo(P2.Nacimiento);
}

Como vis, es muy sencillo trabajar con listas genricas, y realmente resultan muy tiles en muchos
campos.

Por ejemplo, se puede marcar como datasource de cualquier ServerControl de datos, por ejemplo:

Listado.cs
private void arreglaDatos(List<Person> personas)
{
GridView1.DataSource = personas;
GridView1.DataBind();
}

Vous aimerez peut-être aussi