Italian Problema variabile passata per riferimento

Teech

Member
Licensed User
Longtime User
Buongiorno a tutti.
Ho creato una classe per gestire degli oggetti: ho impostato la classe in modo che l'oggetto stesso mi indica se è nuovo, modificato o cancellato.
Ho successivamente creato una classe per gestire una List di questi oggetti (una sorta di collection).
In questa "collection" ho voluto implementare un funzionamento di questo tipo: quando inserisco un oggetto verifico se è nuovo e, se lo è controllo (attraverso delle proprietà) se l'oggetto non è già presente nella lista: se è già presente chiedo all'utente se vuole sovrascriverlo. Per sovrascriverlo pongo le proprietà dell'oggetto trovato ('found') pari a quelle dell'oggetto in inserimento ('item'), poi assegno l'oggetto trovato all'oggetto in inserimento (item=found). Di seguito il codice:
B4X:
Public Sub Add(item As Category)
    If Not(item.IsNew) Then
        mCategories.Add(item)
    Else
        Log("Before check: " & item.IsNew)
        If CheckExist(item) Then
            Log("After check: " & item.IsNew)
            Save(item)
        Else
            If Save(item) Then Insert(item)
        End If
    End If
End Sub
B4X:
Private Sub CheckExist(item As Category) As Boolean
    Private found As Category
    found=Null
    For Each c As Category In mCategories
        If c.Description.ToLowerCase.CompareTo(item.Description.ToLowerCase)=0 Then found=c
    Next

    If Not(found=Null) Then
        If Msgbox2("Category already exists. Overwrite existing category?",item.Description,"Yes","","No",Null)=DialogResponse.POSITIVE Then
            found.Notes=item.Notes
            Log("Before assign: " & item.IsNew)
            item=found
            Log("After assign: " & item.IsNew)
            Return True
        End If
    End If
    Return False
End Sub
Il problema è che nella Sub Add, se CheckExsist ritorna True (dopo una sovrascrittura dell'oggetto 'item') mi aspetto sia stato variato (mi aspetto l'oggetto 'found' della Sub CheckExsist) in quanto è passato per riferimento, ed invece non lo è...
Ho inserito delle scritture nel log per capire ed infatti mi trovo la seguente sequenza:
- Before Check: True
- Before Assign: True
- After Assign: False
- After Check: True

Spero di essere stato chiaro.
Avete idea del perchè il riferimento non viene aggiornato, o meglio, aggiornato e poi ripristinato?
Grazie mille
 
Last edited:

LucaMs

Expert
Licensed User
Longtime User
Il problema sta nel fatto che "found" è locale nella CheckExist, quindi l'assegnazione:
item=found fa sì che item esista solo localmente, penso.

Non ho ben capito perché se l'oggetto non è un nuovo oggetto lo inserisci:
B4X:
    If Not(item.IsNew) Then
        mCategories.Add(item)

ma fa niente, avrai i tuoi motivi :)


Ovviamente, scrivere:
B4X:
found.Notes=item.Notes
è superfluo, visto che subito dopo scrivi:
B4X:
item=found
(a quel punto gli oggetti "saranno uguali", quindi anche la proprietà Notes)


Hai pensato a semplificare il tutto usando una Map?
Dato che per verificare se l'oggetto è in lista usi la proprietà Description, usando questa come chiave di una Map fai molto prima:
B4X:
Dim mapCategories As Map
    mapCategories.Initialize

    dim MyCat As Category
    MyCat.Initialize
    MyCat.Description = "Alimentari"

    mapCategories.Put(MyCat.Description, MyCat)

    If mapCategories.ContainsKey(item.Description) Then
'...



[Per logica e per migliore manutenzione, direi, io all'interno di CheckExist metterei solo la verifica, niente altro - sempreché non decidessi appunto di usare una Map]
 
Last edited:

LucaMs

Expert
Licensed User
Longtime User
Se non vuoi usare una Map, così modificato il tuo codice dovrebbe funzionare:
B4X:
Private Sub CheckExist(item As Category) As Boolean
    For Each c As Category In mCategories
        If c.Description.ToLowerCase.CompareTo(item.Description.ToLowerCase)=0 Then
           If Msgbox2("Category already exists. Overwrite existing category?",item.Description,"Yes","","No",Null)=DialogResponse.POSITIVE Then
               Log("Before assign: " & item.IsNew)
               item = c
               Log("After assign: " & item.IsNew)
               Return True
           End If
       End If
    Next

    Return False
End Sub
 

LucaMs

Expert
Licensed User
Longtime User
Per gli stranieri che leggono anche il forum italiano:

MyCat is not... my cat :D

Funny-Cat-GIFs.jpg
 

Teech

Member
Licensed User
Longtime User
Ti ringrazio molto, effettivamente la Sub CheckExist così è molto più elegante.
Purtroppo però continua a non funzionare.

Quando trovo una uguaglianza e l'utente decide di sovrascrivere, prima assegno le proprietà pubbliche dell'oggetto in elaborazione (item) all'oggetto trovato (c), poi assegno l'oggetto trovato (c) a quello in elaborazione (item) così che le proprietà private vengano poi riportate correttamente. Nello specifico, ogni oggetto Category ha uno status che può essere New, Updated o Deleted (che espongo con delle proprietà di sola lettura) ed in base a queste lancia la query corretta sul DB per aggiornare i dati.

Ho inserito delle scritture sul log per vedere il comportamento delle variabili ed il risultato è il seguente:
Before check: Item.IsNew=true
Before assign: item.IsNew=true - c.IsNew=false
After assign: item.IsNew=false - c.IsNew=false <-- qui esce da CheckExist
After check: Item.IsNew=true
Ho provato anche a mettere una variabile 'found' a livello di modulo e gestire quella invece di 'c' ma ho sempre lo stesso risultato.

Per risolvere posso portare il codice di CheckExsist direttamente nella Sub Add, ma è solo un paliativo, il problema rimane e mi piacerebbe capirne il motivo: ho un oggetto che passo per riferimento e si comporta come se lo passassi per valore, il che mi spiazza.

P.S.: effettivamente il fatto di inserire un oggetto non nuovo ad inizio della Sub Add è un errore (grazie)
PP.SS.: uso una List in quanto il modulo è una sorta di Collection (per i programmatori .NET) ed ho utilizzato l'incapsulamento per gestirla: valuterò sicuramente l'utilizzo di una Map.
 

cimperia

Active Member
Licensed User
Longtime User
Penso che l'errore è perché si sta assegnando un oggetto locale per il parametro. Non funzionerà come l'oggetto locale sarà scartato quando la funzione CheckExist esce.

Invece, assegnare valori al parametro membra funzionerà.

B4X:
Private Sub CheckExist(item As Category) As Boolean
  Private found As Category
  found=Null
  For Each c As Category In mCategories
    If c.Description.ToLowerCase.CompareTo(item.Description.ToLowerCase)=0 Then
      found=c
      exit 'the loop as we have found the category
    end if
  Next

  If Not(found=Null) Then
     If Msgbox2("Category already exists. Overwrite existing category?",item.Description,"Yes","","No",Null)=DialogResponse.POSITIVE Then
       'found.Notes=item.Notes
       Log("Before assign: " & item.IsNew)
       'item=found
       item.IsNew = False
       Log("After assign: " & item.IsNew)
       Return True
     End If
   End If
  Return False
End Sub
 
Last edited:

Teech

Member
Licensed User
Longtime User
Per mantenere la Property IsNew Readonly ho ridotto tutto ad un'unica Sub
B4X:
Public Sub Add(item As Category)
    Dim found As Category=Null
    If item.IsNew Then
        For Each c As Category In mCategories
            If c.Text.ToLowerCase.CompareTo(item.Text.ToLowerCase)=0 Then
                found=c
            End If
        Next
    End If
   
    If found=Null Then
        'Work on item
        If Save(item) Then
            Insert(item)
            mSelected=item
        End If
    Else
        'Work on found
        If Msgbox2("Category already exists. Overwrite existing category?",item.Text,"Yes","","No",Null)=DialogResponse.POSITIVE Then
            found.Notes=item.Notes
            If Save(found) Then
                mSelected=found
            End If
        End If
    End If
End Sub
Grazie dell'aiuto
 

cimperia

Active Member
Licensed User
Longtime User
Bello.

Mi sono preso la libertà di riscrivere un po 'il tuo codice. Forse ti piacciono i piccoli cambiamenti che ho fatto:

B4X:
Public Sub Add(item As Category)
     Dim found As Boolean = False
     If item.IsNew Then
        For Each c As Category In mCategories
           If c.Text.ToLowerCase.CompareTo(item.Text.ToLowerCase)=0 Then
                 found = True
                 Exit 'Exits the For Each Loop as we have found the item.
           End If
      Next
  End If
   
   If Not(found) Then
     'Work on item
        If Save(item) Then
           Insert(item)
           mSelected=item
        End If
   Else
      'Work on c
      If Msgbox2("Category already exists. Overwrite existing category?",item.Text,"Yes","","No",Null)=DialogResponse.POSITIVE Then
        c.Notes=item.Notes
        If Save(c) Then
           mSelected=c
        End If
     End If
  End If
End Sub
 
Top