2010-11-14 13 views
8

Ich möchte wissen, ob ein Äquivalent (.net) existierenVB6 äquivalent zur Liste <someclass>

list<somefixedclass> 

in VB6

Ich weiß, die Sammlung in VB6 existieren, aber es nutzt Objekt (Variante) statt ein bestimmtes Objekt.

danke.

Antwort

21

Es gibt keine direkte Entsprechung in VB 6 zu dem generischen List<T> in VB.NET. In VB 6 gibt es jedoch eine Collection, die ähnliche Funktionen bietet. Der Hauptunterschied ist, dass ein VB 6 Collectionnicht stark typisiert ist, was bedeutet, dass alle Objekte als Variants in der Sammlung gespeichert werden. In einigen Fällen kann dies von Vorteil sein, da Sie damit viele verschiedene Datentypen in derselben Sammlung speichern können. VB verwendet dieses Objekt intern. Es ist einfach genug, ein Collection und up-Cast-Objekte zu verwenden, wie sie aus der Klasse abgerufen werden, aber es gibt wenig, was Sie tun können. Es ist nicht möglich, in der VB-Laufzeit stark typisierte Auflistungen zu implementieren.

Das besagt, es gibt eine Problemumgehung, die Sie implementieren können. Ähnlich wie Collections in früheren Versionen von VB.NET implementiert wurden, bevor Generics eingeführt wurden, können Sie die Collection in eine Klasse einbinden, in der der einzige Zugriff auf das interne Collection durch Methoden erfolgt, die Sie aus dieser Klasse bereitstellen. Dieses Designmuster wird üblicherweise als "benutzerdefinierte Sammlung" bezeichnet.

Dies hat den Vorteil, dass Casting automatisch gehandhabt wird und die Benutzer Ihres Codes davon ablenken, sich Gedanken über Implementierungsdetails zu machen. Es kümmert sich um die (allzu wahrscheinliche) Möglichkeit, dass Sie zur Laufzeit eine Sammlung durchlaufen, die nur einen Objekttyp enthalten soll, aber versehentlich einen zweiten, inkompatiblen Objekttyp hinzugefügt hat, der Ihren Code verursacht eine Ausnahme auslösen Der Nachteil ist natürlich, dass Sie die meisten Funktionen, die bereits vom Collection-Objekt bereitgestellt werden, in Form von öffentlichen Methoden in Ihrer benutzerdefinierten Sammlung erneut implementieren müssen.

Hier ist ein Beispiel dafür, wie Sie darüber gehen könnte:

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 

Beachten Sie, dass, um die benutzerdefinierte Sammlung Item Eigenschaft als die Sammlung der Standardmethode zu setzen (wie der eingebaute in Collection Objekt), müssen Sie folgen diese Schritte in der VB 6 IDE:

  1. Aus dem Menü "Extras" auf "Prozedurattribute"

  2. Wählen Sie den Namen Ihrer benutzerdefinierten Klasse aus dem Kombinationsfeld "Name".

  3. Wenn das Dialogfeld angezeigt wird, klicken Sie auf die Schaltfläche "Erweitert".

  4. Wählen Sie im Kombinationsfeld "Prozedur-ID" den Eintrag "(Standard)".

  5. Klicken Sie auf „OK“


Wenn Sie auch Aufzählung Ihrer benutzerdefinierten Klasse ermöglichen, möchte die For Each Syntax (auch wie die eingebaute in Collection Objekt), Sie hinzufügen NewEnum Funktion, um Ihre eigene Klasse:

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

Sobald Sie das getan haben, was Sie brauchen VB anweisen, diese Eigenschaft zu verwenden:

  1. nach wie vor, öffnen Sie die „Prozedurattribute“ Dialog aus dem Menü „Extras“

  2. Wählen Sie den Namen der benutzerdefinierten Klasse aus der „Name“ Combo-Box.

  3. Wenn das Dialogfeld angezeigt wird, klicken Sie auf die Schaltfläche "Erweitert".

  4. Geben Sie im Kombinationsfeld "Prozedur-ID" die Zahl "-4" ein.

  5. Klicken Sie auf "OK"

+0

+1. Meiner Meinung nach ist diese (richtige) Lösung in vielen Fällen hilfreich, aber in einigen anderen Fällen ist sie vielleicht etwas überdimensioniert. –

+0

@Doc Brown: Einverstanden. Ausgezeichnete Wortwahl. –

+2

+1 Aber erstellen Sie das Muster nicht selbst. Francesco Balena schrieb [CollectionClassMaster] (http://www.angryhacker.com/download/colclassmaster.zip), ein kostenloses Plug-in für die VB6 IDE, das es automatisch für Sie erledigt. Dies wird jetzt freundlicherweise von [AngryHacker] gehostet (http://angryhacker.com/blog/archive/2008/05/01/vb6-swiss-army-knife.aspx)) – MarkJ

0

VB6 ist eine alte Sprache. Es enthält keine Template-ähnlichen Typen wie in modernen Sprachen (C++, C#, Java). Sie müssen also Ihre Objekte als Varianten in der Sammlung speichern und sie später wieder in Ihren Objekttyp umwandeln.

+0

Ich arbeite normalerweise mit C# in verschiedenen Projekten, aber für eigenständige ausführbare Dateien. Warum?. Weil .net ist langsam (in den meisten Fällen), kann es leicht dekompiliert werden, es benötigt das net-Framework, es funktioniert nicht mit den meisten Betriebssystemen, vb6 kann von Windows95 zu Linux (mit Wein ohne viel Aufwand) laufen. Und Java für den Stand allein ausführbar ist bestenfalls "funky". Meine andere Option ist Delphi und C++, aber es ist eine andere Geschichte. – magallanes

+4

Ihre Argumentation ist falsch. VB6 fehlt generische Typen, nicht weil es eine alte Sprache ist (was nicht der Fall ist, C++ ist viel älter), sondern weil es eine bewusste Entscheidung war, diese Art von Komplexität der Sprache zu entziehen, die eine völlig andere Nische hat. – GSerg

+0

Beachten Sie, dass ich nicht downvote, weil -2 mehr als niedrig genug für einen Beitrag ist, der tatsächlich die richtige Antwort bietet.Aber ich denke, es lohnt sich, darauf hinzuweisen, dass wir als Programmierer * nicht * den Luxus haben, die Sprache zu wählen, die wir wollen. Ihr Vorschlag ist nicht wirklich hilfreich und wahrscheinlich frustrierender als alles andere. Und für das, was es wert ist, erinnern Sie sich möglicherweise an das Leben vor Generika in diesem "alten" C# 1.1 ... –

2

EDIT: wenn Cody Grays Lösung für Ihre Bedürfnisse zu sperrig, Sie könnten versuchen, anstatt die „armen Mannes Liste“ Lösung wie folgt:

Dim l() as somefixedclass 
Redim l(0) 

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

Natürlich ein List<somefixedclass> (in C#) oder ein List(Of somefixedclass) in VB.NET ist viel "benutzerfreundlicher", weil es Methoden wie Suchen, Entfernen, AddRange und einige andere hilfreiche Dinge hat. Das alte VB6-Konstrukt geht sehr schlecht mit dem Fall "leere Liste" um. Nicht zu vergessen, Liste < ..> Erhöhung hat eine viel bessere Leistung für große Listen (Größe> 1000).

+0

ja, es ist eine andere Alternative, danke – magallanes

+0

Dies ist eine plausible Lösung, außer dass die Leistung von 'Redim Preserve' kann Killer sein, wenn Sie finden, rufen Sie es viel. Die Verwendung von Arrays oder 'Collection' in VB6 ist eine wichtige Entscheidung für das Design. Wenn Sie die Anzahl der Elemente, die Sie im Voraus haben, kennen (oder sich annähern können), sollten Sie sich für ein Array entscheiden. Die Leistung ist etwas besser als eine "Sammlung", aber wenn Sie Objekte dynamisch hinzufügen und entfernen, ist eine 'Sammlung' wahrscheinlich die beste Wahl. –

+0

@Cody Gray: ja, Leistung kann auf diese Weise schlecht werden. In den meisten realen Programmen, die ich in den letzten 5 Jahren in Excel-VBA geschrieben habe, ist der 98% ige Fall einer Liste eine Liste mit weniger als 500 Einträgen, wobei diese Lösung vollkommen ausreichend ist. –

4

Hier ist unsere Implementierung von Arraylist. Sie können es als Basis (nicht durch Vererbung natürlich, aber durch Komposition wie in CodyGray's Antwort ausgedrückt) für eine stark typisierte Klasse verwenden, aber wenn Sie keine Typsicherheit benötigen, ist es viel besser als die Collection-Klasse.

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 Interessante Lösung. Nur aus Neugierde: Haben Sie dies als schneller für Add-Ons/Insertions als die 'Collection' Klasse bewertet? Oder auf welche Weise meintest du, dass es besser ist? –

+0

In welcher Weise ist es besser? Die Sammlung kann sich ohne Ihr Wissen in ein Wörterbuch verwandeln. Dann ist Ihre Zählung, sagen wir 5, aber die Elemente sind (0,1,4,5,6) Sie erhalten einen Fehler, wenn Sie versuchen, auf Element 3 zuzugreifen und einen Fehler zu erhalten. Es ist auch schneller, wenn Sie GrowSize so einstellen, dass es für die Größe Ihres Arrays geeignet ist (yeah, es könnte sich wahrscheinlich verbessern, indem es jedes Mal, wenn es wächst, verdoppelt, aber das ist eine Mikrooptimierung, die den zusätzlichen Code nicht wert ist). Jedoch in diesen Tagen (als es geschrieben wurde, war die Geschwindigkeit einer CPU ungefähr 200Mhz) ist die Effizienz nicht das, was wichtig ist, keinen Fehler zu haben. –

Verwandte Themen