2010-11-14 26 views

Respuesta

21

No existe un equivalente directo en VB 6 al genérico List<T> que se encuentra en VB.NET. Sin embargo, existe una cosa como Collection en VB 6, que proporciona una funcionalidad similar. La principal diferencia es que un VB 6 Collection es no fuertemente tipado, lo que significa que todos los objetos se almacenan como Variants en la colección. En algunos casos, esto puede ser beneficioso, ya que le permite almacenar muchos tipos diferentes de datos en la misma colección, y de hecho, VB utiliza este objeto internamente. Es bastante fácil usar un Collection y objetos mejorados a medida que se recuperan de la clase, pero hay poco que puede hacer. No es posible implementar colecciones fuertemente tipadas en el tiempo de ejecución de VB.

Habiendo dicho eso, hay una solución que puede implementar. De forma similar a cómo se implementaron las colecciones en las versiones anteriores de VB.NET antes de que se introdujeran los genéricos, puede envolver el Collection en una clase, donde el único acceso al interno Collection es a través de los métodos que expone de esta clase. Este patrón de diseño se conoce comúnmente como "colección personalizada".

Esto tiene la ventaja de manejar automáticamente el casting, y alivia a los consumidores de su código de tener que recordar importar detalles de implementación como este. Se ocupa de la (muy probable) posibilidad de que recorra una colección en tiempo de ejecución que solo se supone que contiene un tipo de objeto, pero accidentalmente se agregó un segundo tipo de objeto incompatible que causa su código lanzar una excepción Por supuesto, la desventaja es que debe volver a implementar la mayor parte de la funcionalidad que ya proporciona el objeto Collection, en forma de métodos públicos en su colección personalizada.

He aquí un ejemplo de cómo puede ir sobre eso:

Public Class CustomerCollection 

    ''#Internal collection, exposed by this class 
    Private m_Customers As Collection 

    Private Sub Class_Initialize() 
     ''#Set up the internal collection 
     Set m_Customers = New Collection 
    End Sub 

    Public Sub Add(ByVal cust as Customer, Optional ByVal key as String) 
     ''#Add the Customer object to the internal collection 
     If IsMissing(key) Then 
      m_Customers.Add cust 
     Else 
      m_Customers.Add cust, key 
     End If 
    End Sub 

    Public Property Get Count() As Integer 
     ''#Return the number of objects in the internal collection 
     Count = m_Customers.Count 
    End Property 

    Public Sub Remove(ByVal index As Variant) 
     ''#Remove the specified object from the internal collection, 
     ''# either by its index or its key 
     m_Customers.Remove index 
    End Sub 

    Public Function Item(ByVal index As Variant) as Customer 
     ''#Return the specified object from the internal collection, 
     ''# either by its index or its key 
     Set Item = m_Customers.Item(index) 
    End Function 

    Public Sub Clear() 
     ''#Removes all objects from the internal collection 
     Set m_Customers = New Collection 
    End Sub 

End Class 

Tenga en cuenta que con el fin de establecer la propiedad de la colección personalizada Item como método predeterminado de la colección (como el incorporado en Collection objeto), es necesario seguir estos pasos en el IDE de VB 6:

  1. de los menú "Herramientas", haga clic en "Atributos del procedimiento"

  2. Seleccione el nombre de su clase personalizada en el cuadro combinado "Nombre".

  3. Cuando aparece el cuadro de diálogo, haga clic en el botón "Avanzado".

  4. Seleccione el elemento "(Predeterminado)" en el cuadro combinado "ID de procedimiento".

  5. Haga clic en "Aceptar"


Si también le gustaría permitir la enumeración de la clase personalizada utilizando la sintaxis For Each (también como la incorporada en Collection objeto), se puede añadir una NewEnum función de la clase personalizada:

Public Property Get NewEnum() As IUnknown 
    ''#Provides support for enumeration using For Each 
    Set NewEnum = m_Customers.[_NewEnum] 
End Property 

Una vez que hayas hecho esto, es necesario instruir a VB para utilizar esta propiedad:

  1. Al igual que antes, abra el diálogo "Atributos del procedimiento" en el menú "Herramientas"

  2. Seleccione el nombre de la clase personalizada en el cuadro combinado "Nombre".

  3. Cuando aparece el cuadro de diálogo, haga clic en el botón "Avanzado".

  4. Escriba el número "-4" en el cuadro combinado "ID de procedimiento".

  5. Haga clic en "Aceptar"

+0

+1. En mi humilde opinión, esta solución (correcta) es útil en muchos casos, pero en otros casos puede ser un poco demasiado grande. –

+0

@Doc Brown: De acuerdo. Excelente elección de palabras. –

+2

+1 Pero no cree el texto repetitivo usted mismo. Francesco Balena escribió [CollectionClassMaster] (http://www.angryhacker.com/download/colclassmaster.zip), un complemento gratuito para el IDE VB6 que lo hará automáticamente. Esto ahora es amablemente organizado por [AngryHacker] (http://angryhacker.com/blog/archive/2008/05/01/vb6-swiss-army-knife.aspx)) – MarkJ

0

VB6 es un lenguaje antiguo. No contiene tipos de plantilla como los hay en los idiomas modernos (C++, C#, Java). Por lo tanto, deberá almacenar sus objetos como variantes en la colección y luego volver a enviarlos a su tipo de objeto.

+0

Normalmente trabajo con C# en varios proyectos, pero para ejecutables independientes. ¿Por qué?. Debido a que .net es lento (en la mayoría de los casos), se puede descompilar fácilmente, requirió el marco de red, no funciona con la mayoría de los sistemas operativos, vb6 puede ejecutarse desde Windows95 a Linux (con vino sin mucho esfuerzo). Y Java para soporte ejecutable solo es "funky" en el mejor de los casos. Mi otra opción es Delphi y C++, pero es otra historia. – magallanes

+4

Su razonamiento es incorrecto. VB6 carece de tipos genéricos, no porque sea un lenguaje antiguo (lo que no es, C++ es mucho más antiguo), sino porque fue una decisión consciente para mantener este tipo de complejidades lejos del lenguaje que tiene un nicho completamente diferente. – GSerg

+0

Tenga en cuenta que no he votado negativamente, porque -2 es más que suficiente para una publicación que sí proporciona la respuesta correcta.Pero creo que vale la pena señalar que nosotros como programadores no * siempre * tenemos el lujo de elegir el idioma que queramos. Su sugerencia no es realmente útil, y probablemente más frustrante que cualquier otra cosa. Y por lo que vale, es posible que recuerde la vida antes de los genéricos en ese "antiguo" C# 1.1 ... –

2

EDIT: si la solución de Cody Gray es también de gran tamaño para sus necesidades, puede intentar en su lugar "lista de los pobres" solución de la forma siguiente:

Dim l() as somefixedclass 
Redim l(0) 

'... 
'increase size dynamically: 
Redim Preserve l(ubound(l)+1) 
'... 

Por supuesto, un List<somefixedclass> (en C#) o un List(Of somefixedclass) en VB.NET es mucho más "fácil de usar" porque tiene métodos como Buscar, Eliminar, Agregar Rango y algunas otras cosas útiles. La antigua construcción VB6 trata muy mal con el caso de la "lista vacía". No lo olvides, List < ..> el incremento tiene un rendimiento mucho mejor para listas grandes (tamaño> 1000).

+0

sí, es otra alternativa, gracias – magallanes

+0

Esta es una solución plausible, excepto que el rendimiento de 'Redim Preserve' puede ser fatal si te encuentras llamando mucho. El uso de matrices o una "Colección" en VB 6 es una decisión de diseño importante, y el mejor consejo es que, si conoce (o puede aproximarse bastante) a la cantidad de elementos que tendrá por adelantado, debe optar por una matriz. El rendimiento es ligeramente mejor que una 'Colección', pero si agrega y elimina dinámicamente objetos, una 'Colección' es probablemente la mejor opción. –

+0

@Cody Gray: sí, el rendimiento puede ser malo de esta manera. En los programas más reales que escribí en los últimos 5 años en Excel-VBA, el 98% de los casos de una lista es una lista con menos de 500 entradas, donde esta solución es completamente suficiente. –

4

Aquí es nuestra implementación de ArrayList. Puede usarlo como una base (no a través de la herencia, obviamente, sino a través de la composición como se expresa en la respuesta de CodyGray) para una clase fuertemente tipada, pero si no necesita seguridad tipo es mucho mejor que la clase Collection.

Option Explicit 

Private mavInternalArray() As Variant 
Private mlArraySize As Long 
Private mlCount As Long 
Private mlGrowSize As Long 
Private mfZeroIndex As Boolean 

'--------------------------------------------------------------------------------------- 
' Procedure Clear 
'--------------------------------------------------------------------------------------- 
Public Sub Clear() 
      Dim index As Long 

     For index = 0 To mlCount - 1 
      If IsObject(mavInternalArray(index)) Then 
       Set mavInternalArray(index) = Nothing 
      End If 
     Next index 
     mlCount = 0 

End Sub 



'--------------------------------------------------------------------------------------- 
' Procedure Swap 
'--------------------------------------------------------------------------------------- 
Public Sub Swap(Index1 As Long, index2 As Long) 
      Dim vTmp As Variant 


     If IsObject(mavInternalArray(index2)) Then 
      Set vTmp = mavInternalArray(index2) 
     Else 
      vTmp = mavInternalArray(index2) 
     End If 

     If IsObject(mavInternalArray(Index1)) Then 
      Set mavInternalArray(index2) = mavInternalArray(Index1) 
     Else 
      mavInternalArray(index2) = mavInternalArray(Index1) 
     End If 

     If IsObject(vTmp) Then 
      Set mavInternalArray(Index1) = vTmp 
     Else 
      mavInternalArray(Index1) = vTmp 
     End If 


End Sub 

Public Property Get ZeroIndex() As Boolean 
     ZeroIndex = mfZeroIndex 
End Property 

Public Property Let ZeroIndex(fZeroIndex As Boolean) 
     mfZeroIndex = fZeroIndex 
End Property 

Public Property Get GrowSize() As Long 
     GrowSize = mlGrowSize 
End Property 

Public Property Let GrowSize(lNewSize As Long) 
     Debug.Assert lNewSize > 0 
     mlGrowSize = lNewSize 
End Property 

Private Sub Class_Initialize() 
     mlGrowSize = 50 
     mlArraySize = mlGrowSize 
     mfZeroIndex = True 
     mlCount = 0 


     ReDim mavInternalArray(0 To mlGrowSize - 1) 

End Sub 

'--------------------------------------------------------------------------------------- 
' Procedure Remove 
'--------------------------------------------------------------------------------------- 
Public Sub Remove(index As Long) 
     Dim index2 As Long 


     For index2 = index To mlCount - 2 
      If IsObject(mavInternalArray(index2 + 1)) Then 
       Set mavInternalArray(index2) = mavInternalArray(index2 + 1) 
      Else 
       mavInternalArray(index2) = mavInternalArray(index2 + 1) 
      End If 
     Next index2 
      If mlCount <= 0 Then 
      Exit Sub 
      End If 
     mlCount = mlCount - 1 
     If IsObject(mavInternalArray(mlCount)) Then 
      Set mavInternalArray(mlCount) = Nothing 
     Else 
      mavInternalArray(mlCount) = False 
     End If 
End Sub 

'--------------------------------------------------------------------------------------- 
' Procedure Items 
'--------------------------------------------------------------------------------------- 
Public Function Items(index As Long) As Variant 
     If Not mfZeroIndex Then 
      index = index - 1 
     End If 

     If index < mlCount And index >= 0 Then 
      If IsObject(mavInternalArray(index)) Then 
       Set Items = mavInternalArray(index) 
      Else 
       Items = mavInternalArray(index) 
      End If 
     End If 
End Function 

Public Sub SetItem(index As Long, Item As Variant) 
     If Not mfZeroIndex Then 
      index = index - 1 
     End If 
     If IsObject(Item) Then 
      Set mavInternalArray(index) = Item 
     Else 
      mavInternalArray(index) = Item 
     End If 
End Sub 

'--------------------------------------------------------------------------------------- 
' Procedure Add 
'--------------------------------------------------------------------------------------- 
Public Function Add(vItem As Variant) As Long 

     mlCount = mlCount + 1 
     If mlCount > mlArraySize Then 
      mlArraySize = mlArraySize + mlGrowSize 
      ReDim Preserve mavInternalArray(0 To mlArraySize - 1) 
     End If 

     If IsObject(vItem) Then 
      Set mavInternalArray(mlCount - 1) = vItem 
     Else 
      mavInternalArray(mlCount - 1) = vItem 
     End If 

     Add = mlCount - 1 

End Function 

'--------------------------------------------------------------------------------------- 
' Procedure ItemArray 
'--------------------------------------------------------------------------------------- 
Public Function ItemArray() As Variant 
     Dim vReturnArray As Variant 

     vReturnArray = mavInternalArray 
     ReDim Preserve vReturnArray(0 To mlCount - 1) 
     ItemArray = vReturnArray 
End Function 

Public Function Count() As Long 
     Count = mlCount 
End Function 


'--------------------------------------------------------------------------------------- 
' Procedure Insert 
'--------------------------------------------------------------------------------------- 
Public Function Insert(index As Long, vItem As Variant) As Long 
     Dim index2 As Long 

     'Make sure array is large enough for a new item 
     mlCount = mlCount + 1 
     If mlCount > mlArraySize Then 
      mlArraySize = mlArraySize + mlGrowSize 
      ReDim Preserve mavInternalArray(0 To mlArraySize - 1) 
     End If 

     'Bump all the items with a higher index up one spot 

     If index >= mlCount - 1 Then 
      If IsObject(vItem) Then 
       Set mavInternalArray(mlCount - 1) = vItem 
      Else 
       mavInternalArray(mlCount - 1) = vItem 
      End If 
     Else 

      For index2 = mlCount - 1 To index + 1 Step -1 
       If IsObject(vItem) Then 
        Set mavInternalArray(index2) = mavInternalArray(index2 - 1) 
       Else 
        mavInternalArray(index2) = mavInternalArray(index2 - 1) 
       End If 
      Next index2 

      If IsObject(vItem) Then 
       Set mavInternalArray(index) = vItem 
      Else 
       mavInternalArray(index) = vItem 
      End If 
     End If 
     Insert = mlCount - 1 

End Function 


Public Sub Clone(ByRef cDestinationDynamicArray As clsDynamicArray) 
     Dim index As Long 

     If cDestinationDynamicArray Is Nothing Then 
      Set cDestinationDynamicArray = New clsDynamicArray 
     End If 

     cDestinationDynamicArray.Clear 

     For index = 0 To mlCount - 1 
      Call cDestinationDynamicArray.Add(mavInternalArray(index)) 
     Next index 

End Sub 

Public Property Get NewEnum() As IUnknown 
    ''#Provides support for enumeration using For Each 
    Set NewEnum = m_Customers.[_NewEnum] 
End Property 
+0

+1 Solución interesante. Solo por curiosidad: ¿Ha evaluado esto como más rápido para adiciones/inserciones que la clase 'Colección'? ¿O de qué manera quisiste decir que era mejor? –

+0

¿De qué manera es mejor? La colección puede convertirse accidentalmente en un diccionario, sin que usted lo sepa. Entonces su recuento es, digamos 5, pero los ítems son (0,1,4,5,6) cuando se obtiene un error al intentar acceder al elemento 3 y obtener un error. Además, es más rápido si configura GrowSize para que sea apropiado para el tamaño de su matriz (sí, probablemente podría mejorar duplicando el crecimiento cada vez que crezca, pero eso es una micro-optimización que no vale la pena el código adicional). Sin embargo, en estos días (cuando se escribió que la velocidad de una CPU era de alrededor de 200Mhz) la eficiencia no es lo importante, no tener un error. –

Cuestiones relacionadas