VB.NET 2008 Applicazione crash durante Do Loop
Domanda
vi scrivo un'applicazione per confrontare ciascun elemento ListBox1 a tutti gli elementi su ListBox2. Se l'articolo è trovato, quindi elimina da entrambe le liste. L'obiettivo è quello di avere solo gli elementi che non sono stati trovati rimangono in entrambe le liste.
Il problema è, l'applicazione si blocca solo e non ho mai ottenere alcun risultato. Ho guardato il mio codice più volte e non riesco a capire cosa sta succedendo (programmazione niubbo lo so ...).
Qualcuno può aiutarmi a questo?
Snippet di codice:
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
Dim a As String
Dim b As String
Dim y As String
For i As Integer = 0 To ListBox1.Items.Count - 1
a = ListBox1.Items(i)
y = 1
Do While y = 1
For x As Integer = 0 To ListBox2.Items.Count - 1
b = ListBox2.Items(x)
Dim res As Int16 = String.Compare(a, b)
If res = 0 Then
y = 0
ListBox2.Items.Remove(i)
ListBox2.Items.Remove(x)
ElseIf x = ListBox1.Items.Count Then
Exit Do
End If
Next
Loop
Next
End Sub
Soluzione
se ListBox1.Items.Count è più che ListBox2.Items.Count - 1, X non sarà mai uguale ListBox1.Items.Count, quindi l'uscita Do non potrà mai funzionare, e il codice sarà solo ciclo senza fine nel
Do While y = 1
Hai pensato di usare LINQ per esempio, per la gestione delle liste più facile?
modifica:. Inoltre è sbagliato eliminare un elemento dalla lista che si sta movimento con una per (è addirittura illegale di farlo con un For Each) perché ogni eliminazione compenserà il contatore del ciclo
EDIT2: Ecco un frammento di Linq che compie l'operazione:
Dim itemsFirst = (From item As String In ListBox1.Items Select item)
Dim itemsSecond = (From item As String In ListBox2.Items Select item)
Dim dupes = System.Linq.Enumerable.Intersect(itemsFirst, itemsSecond).ToList
For Each item In dupes
ListBox1.Items.Remove(item)
ListBox2.Items.Remove(item)
Next item
ciò che è non è fondamentalmente estrarre le stringhe di entrambe lista (questo è necessario perché la collezione ListBox.Items è un po 'strano)
Dopo che corriamo il metodo Intersect e copiare i risultati in una lista. (La parte ToList) La copia è una parte necessaria perché, altrimenti gonzi sarebbe solo un sottoinsieme degli elementi della ListBox, e ancora una volta ci sarebbe cercare di tirarsene tirando nostri shoestrings.
L'ultima parte è solo un semplice ciclo di eliminazione, che rimuove gli elementi della collezione
Altri suggerimenti
si deve
ElseIf x = ListBox1.Items.Count Then
Exit Do
quando dovrebbe essere
ElseIf x = ListBox1.Items.Count - 1 Then
Exit Do
perché il ciclo for cambierà X a contare, e poi uscire senza l'iterazione a quel valore.
Non solo, ma perché c'è un ciclo Do
comunque? Non c'è alcuna necessità di mantenere l'iterazione stessa casella di riepilogo interno alla ricerca di duplicati, è?
E in terzo luogo, non si deve rimuovere le cose mentre si scorre attraverso di loro. Nel tuo caso la cicli for sono il riutilizzo di conteggio, quindi è "sicuro", ma l'operazione di rimozione sarà reindex cose, così si dovrebbe sottrarre 1 dal ie x iteratori quando si rimuove, in modo che il prossimo non è saltato dalla reindicizzazione .
Il secondo pensiero, forse si mette quel ciclo Do
lì per coprire gli elementi saltato la volta precedente in giro, come accennato nel mio terzo punto.
Se stai usando Visual Studio 2008 o successivamente:
Dim dupes = Listbox1.Items.Cast(Of String)().Intersect(Listbox2.Items.Cast(Of String)()).ToArray()
For Each item As String in dupes
Listbox1.Items.Remove(item)
Listbox2.Items.Remove(item)
Next item
I correva una prova di tre diversi metodi. Sono Joels, SWEKO, e la mia. Stavo facendo questo per le prestazioni del test, ma ho scoperto che i risultati non sono gli stessi, le listboxes non sono gli stessi . Ecco il codice che ho usato per testare, in modo da poter essere il giudice. Probabilmente qualche errore stupido da parte mia.
Dim stpw As New Stopwatch
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
Debug.WriteLine("")
loadLBTD() ''#load test data
doSTPW("Test 1 Start", False) ''#mine
''#get rid of dupes <<<<<<<<<<<<<<
Dim dupeL As New List(Of String)
For x As Integer = ListBox1.Items.Count - 1 To 0 Step -1
If ListBox2.Items.Contains(ListBox1.Items(x)) Then
dupeL.Add(ListBox1.Items(x))
ListBox1.Items.RemoveAt(x)
End If
Next
For Each s As String In dupeL
ListBox2.Items.Remove(s)
Next
doSTPW("Test 1 End")
loadLBTD() ''#load test data
doSTPW("Test 2 Start", False) ''#sweko
''#get rid of dupes <<<<<<<<<<<<<<
''#I had to set Option Strict to Off to get this to work <<<<<<<
Dim itemsFirst = (From item As String In ListBox1.Items Select item)
Dim itemsSecond = (From item As String In ListBox2.Items Select item)
Dim dupes = System.Linq.Enumerable.Intersect(itemsFirst, itemsSecond).ToList
For Each item In dupes
ListBox1.Items.Remove(item)
ListBox2.Items.Remove(item)
Next item
doSTPW("Test 2 End")
loadLBTD() ''#load test data
doSTPW("Test 3 Start", False) ''#joel
''#get rid of dupes <<<<<<<<<<<<<<
Dim dupes2 = ListBox1.Items.Cast(Of String)().Intersect(ListBox2.Items.Cast(Of String)()).ToArray()
For Each item As String In dupes2
ListBox1.Items.Remove(item)
ListBox2.Items.Remove(item)
Next item
doSTPW("Test 3 End")
End Sub
Private Sub doSTPW(ByVal someText As String, Optional ByVal showTM As Boolean = True)
stpw.Stop() ''#stop the clock
If flip Then Debug.Write("'T ") Else Debug.Write("'F ")
Debug.Write("LBCnts " & ListBox1.Items.Count & " " & ListBox2.Items.Count)
Dim s As String
If showTM Then
s = String.Format(" {0} {1}", someText, stpw.ElapsedTicks.ToString("N0"))
Else
s = String.Format(" {0}", someText)
End If
Debug.WriteLine(s)
stpw.Reset() ''#reset and start clock
stpw.Start()
End Sub
Dim flip As Boolean = False
Private Sub loadLBTD()
''#Create test data
Dim tl1() As String = New String() {"A", "X", "y", "z", "B", "w", "X", "y", "z"}
Dim tl2() As String = New String() {"A", "y", "z", "Q", "A", "y", "z", "Q", "A", "y", "z", "Q"}
ListBox1.Items.Clear()
ListBox2.Items.Clear()
''#load listboxes
If flip Then
ListBox1.Items.AddRange(tl2)
ListBox2.Items.AddRange(tl1)
Else
ListBox1.Items.AddRange(tl1)
ListBox2.Items.AddRange(tl2)
End If
''#end of test data setup
End Sub
Inoltre, come previsto, LINQ è più conciso ma più lento. Se il codice viene utilizzato di rado non ha importanza. Ho avuto una brutta esperienza con LINQ e Crivello di Eratostene.