Ambiguità sematica del ciclo For Each?
Anche questa è una divagazione di mezza estate. Riporto quasi pari pari il carteggio fra me e Francesco Balena.
Una cosuccia strana non scritta da nessuna parte. Il ciclo For Each... Next si direbbe che funzioni solo IN LETTURA, come nello snippet seguente:
Sub Main()
Dim MiaLista As New List(Of Integer)
Dim V() As Integer = {1, 2, 3, 4, 5}
For i = 0 To UBound(V)
MiaLista.Add(V(i))
Next
For Each el As Integer In MiaLista
el *= 2 ' Accolta “sulla carta”, ma senza REALI conseguenze
Next
For Each el As Integer In MiaLista
Console.WriteLine(el) ' Continua a indicare 1, 2, 3, 4, 5
Next
Console.WriteLine()
' Solo con gl’indici le cose vanno bene:
For i = 0 To MiaLista.Count - 1
MiaLista(i) *= 2
Next
For Each el As Integer In MiaLista
Console.WriteLine(el) ' Indica 2, 4, 6, 8, 10
Next
Console.ReadLine()
End Sub
La stessa delusione di aspettative (ingenue?) si ha con le matrici, del tipo Dim (V).
Qualcuno avrà l’onestà di ammettere che non lo sapeva? Forse e, magari, i più esperti (per lo più dimentichi di segnalare la cosa all’inclita e al volgo) forniranno dotte delucidazioni.
Ma attenzione! Non è finita. Infatti con le Liste (e simili) di OGGETTI le cose funzionano come ci si attende:
Module Module1
Class Persona
Public Nome As String
End Class
Sub Main()
Dim VettNomiPers() = {"Ciccio", "Maria", "Luigi", "Paola"}
Dim Persone As New List(Of Persona)
Dim i As Integer = 0
Dim UnaPersona As New Persona
' Carica VettNomiPers nella lista Persone
For Each NomePers As String In VettNomiPers
UnaPersona.Nome = NomePers
Persone.Add(UnaPersona)
UnaPersona = New Persona
Next
For Each UnaPersona In Persone
Console.WriteLine(UnaPersona.Nome) ' => "Ciccio", "Maria" ecc.
UnaPersona.Nome = "Ciao" ' => "Ciao"
Console.WriteLine(UnaPersona.Nome)
Next
Console.WriteLine()
Console.WriteLine("LISTA MODIFICATA:")
For Each UnaPersona In Persone
Console.WriteLine(Persone(i).Nome) ' => Tutti Ciao
Next
Console.ReadLine()
End Sub
End Module
NOTA – Funzionamento OK anche con ciclo finale indicizzato (ovvio) e con modifiche apportate a un eventuale campo numerico.
Come utenteilmente segnalerei la presenza di un problema semantico, con l’aggravante della contraddizione indicata nell’ultimo esempietto.
Do not forget, anyway.
LA (GIUSTA) REPLICA DI FRANCESCO BALENA:
Mi dispiace deluderti, ma questa feature è ben documentata. Basta andare a leggere il manuale ufficiale del linguaggio:
http://msdn.microsoft.com/en-us/library/5ebk1751.aspx
Modifying Collection Elements. The Current property of the enumerator object is ReadOnly (Visual Basic), and it returns a local copy of each collection element. This means that you cannot modify the elements themselves in a For Each...Next loop. Any modification you make affects only the local copy from Current and is not reflected back into the underlying collection. However, if an element is a reference type, you can modify the members of the instance to which it points. The following example illustrates this.
Il fatto che “sembri” funzionare con gli oggetti è una pura illusione, derivante probabilmente da un equivoco abbastanza comune tra i programmatori. Nel tuo esempio tu non modifichi la variabile “el” ma modifichi l’N-esimo elemento della lista a cui “el” punta. Non è un dettaglio da poco, anzi fa tutta la differenza.
Volendo fare una analogia, “el” è l’indirizzo fisico di un negozio con vetrina e ingresso, che oggi contiene un fruttivendolo. “el” è a sola lettura e infatti il numero civico di quella vetrina non cambierà mai, ma il contenuto di quel negozio può liberamente cambiare ed ospitare ad esempio una farmacia. Non a caso, si dice che una variabile “punta” a qualcosa, non che “contiene” qualcosa.
[ Per la cronaca, resta il fatto che in quasi nessun altro posto ho trovato queste precisazioni, comunque da NON obliare, anzi il più delle volte si lascia credere che For Each e For i = 0 To siano equivalenti... Beh dipende... ]
?>