Jump to content
Sign in to follow this  
guest

Stranezza in C#

Recommended Posts

Sto facendo una stupidaggine in C#, giusto per provare (non sembra male come linguaggio).

 

Si verifica, però, una cosa strana:

 

using (WebClient webclient = new WebClient())
{
  ... un po di codice ...

  webclientHandler webclientHandler = new webclientHandler();
  webclientHandler.lista = listView;
  webclientHandler.nomeItem = item.Name;

  webclient.DownloadProgressChanged += webclientHandler.DownloadProgressChanged;

  ... altro codice ...
}

 

in pratica, creo un oggetto webclient e creo un oggetto webclientHandler al quale attacco alcuni miei settaggi.

Poi passo il metodo "DownloadProgresChanged" di webclientHandler a webclient.DownloadProgressChanged.

In sostanza, attacco un mio metodo ad un evento predefinito di .NET

 

Tutto il codice di cui sopra è dentro un ciclo, che legge determinati valori e crea tanti oggetti per ciascun valore (ad esempio il download di 3 file)

 

La stranezza è questa:

se eseguo il codice così com'è, il download parte solo del primo file della lista, se invece introduco un banale ritardo (System.Threading.Thread.Sleep(5000);) subito sopra la creazione dell'oggetto webclientHandler partono anche tutti gli altri download.

 

E' come se, senza ritardo, non venissero creati nuovi oggetti.

 

Che caspita può essere?

Share this post


Link to post
Share on other sites

Mi sfugge qualcosa nel codice, prova a copiare le righe in cui richiami il metodo del webclient.

 

Comunque visto così potrei pensare che l'esecuzione debba proseguire fuori dal blocco using, con lo sleep si dia tempo all'oggetto di entrare in una condizione tale da non poter essere cancellato/deallocato finito il blocco using.

Share this post


Link to post
Share on other sites

Il blocco using l'ho messo proprio per evitare possibili "riutilizzi" del mio oggetto (ovvero, la prima volta vien creato e la seconda in realtà viene utilizzato l'oggetto già esistente, nonostante il "new")

 

Comunque sia, questo è il codice di tutto il bottone che esegue l'operazione:

 


           foreach (ListViewItem item in lstViewDownloads.Groups["paused"].Items)
           {
               string nomefile = System.IO.Path.GetTempPath() + Guid.NewGuid().ToString();
               /*nomefile = nomefile = System.IO.Path.GetTempPath()+"prova.txt";*/
               /*MessageBox.Show("Scarico in: " + nomefile);*/

               /* Scarico tutti i file in sospeso */
               WebClient webclient = new WebClient();

               Console.WriteLine("ID: {0}, download avviato.", item.Name);
               /*System.Threading.Thread.Sleep(1000);*/
               /*MessageBox.Show("itemName: " + item.Name);*/
               webclientHandler webclientHandler = new webclientHandler();
               webclientHandler.lstViewDownloads = lstViewDownloads;
               webclientHandler.nomeFile = nomefile;
               webclientHandler.nomeItem = item.Name;
               webclientHandler.startTime = DateTime.Now;

               webclient.DownloadFileCompleted += webclientHandler.DownloadFileCompleted;
               webclient.DownloadProgressChanged += webclientHandler.DownloadProgressChanged;
               webclient.DownloadFileAsync(new Uri(item.Text), nomefile);

               item.SubItems[1].Text = "Download in corso...";

               webclientHandler = null;
               webclient = null;

           }

Share this post


Link to post
Share on other sites

Se ho capito bene lo scopo è fare il download usando il metodo asincrono, il problema quindi è che la vita dell'oggetto non deve finire all'interno del ciclo.

 

Con il blocco using viene chiamato un Dispose() sull'oggetto all'uscita, lo scopo dell'istruzione è anche quello, probabilmente senza sleep non faceva in tempo a partire il download perchè l'oggetto si distruggeva prima.

 

Per le variabili dichiarate e il funzionamento in c# nei cicli non mi sono mai posto il problema, programmo principalmente in vb.net e li la variabile è sempre la stessa, cambia l'oggetto puntato.

 

Comunque con l'uso di metodi asincroni l'oggetto va referenziato da qualche parte se no finito fuori dal suo scope il garbage collector prima o poi farà il suo lavoro e l'oggetto non ci sarà più.

 

Potresti far puntare la proprietà Tag del ListViewItem a qualcosa che referezia poi sia il webclient sia il webclientHandler.

Share this post


Link to post
Share on other sites

Aspetta, forse ho omesso un particolare.

Io ho una listview con al suo interno un certo numero di file da scaricare (il come ci arrivano dentro la listview, non ha importanza in questo discorso).

 

Come puoi vedere dal ciclo (richiamato da dentro un bottone "Scarica") io faccio un foreach per ciascun elemento della listview e creo un oggetto WebClient avviando quindi il download.

 

Creo un oggetto per ciascun file da scaricare e gli associo alcuni miei metodi in modo da monitorare lo status e la % di download.

 

Potrei provare a creare un oggetto dal nome dinamico, anzichè riciclare sempre la stessa variabile. Ma come si fa?

 

Come posso fare un "WebClient <nomedinamico> = new WebClient" ?

 

E comunque, se fosse un problema di persistenza dell'oggetto, avrei un comportamento differente, ovvero lui parte con i download poi subito dopo lo ammazza avviando il nuovo. Io invece riesco ad avviare sempre il primo download (ed a completarlo) e mai il secondo, ovvero il nuovo oggetto o non viene creato oppure non viene eseguito.

Al limite, con lo sleep riesco a far partire i primi due oggetti (ed a terminarne il download) ma mai un terzo, quindi non penso sia un problema di oggetti che si sovrascrivono, altrimenti il secondo download ucciderebbe il primo e gli monterebbe sopra.

Share this post


Link to post
Share on other sites

Il problema è che quando la variabile è riutilizzata, il vecchio oggetto continua a esistere solo se è referenziato da qualche altra variabile, nel caso del webClient da quanto scrivi direi che se parte il download asincrono allora esiste fino alla fine.

 

In conclusione vanno tenuti i riferimenti agli oggetti webClient e webclientHandler, perchè nel momento in cui non ci sono più variabili che puntano a loro sono destinati a essere distrutti.

 

La prima idea che mi viene in mente è:

- mettere nel webClientHandler un riferimento al webclient associato ad esempio con una variabile pubblica o una proprietà

- utilizzare la proprietà Tag del ListViewItem per poi farla puntare all'oggetto webClientHandler

- in base a come funziona questo webClientHandler mettergli un'eventuale proprietà che contenga il riferimento al ListViewItem nel caso debba accedere a questo oggetto.

 

per fare poi qualcosa del tipo:

 

WebClient webclient = new WebClient();
webclientHandler webclientHandler = new webclientHandler();

....

[b]webclientHandler.webClientAssociato = webclient;
webclientHandler.listViewItemAssociato = item;
item.Tag = webclientHandler;[/b]

webclient.DownloadFileCompleted += webclientHandler.DownloadFileCompleted;
webclient.DownloadProgressChanged += webclientHandler.DownloadProgressChanged;
webclient.DownloadFileAsync(new Uri(item.Text), nomefile);

Il webClientHandler finito il download poi potrebbe togliere il suo stesso riferimento dal parametro Tag del listviewitem con listViewItemAssociato.Tag = null;

Share this post


Link to post
Share on other sites

Però facendo come dici te, ovvero usando il tag, potrei associare un solo webclient per volta e quindi un solo download per volta... io devo fare un download contemporaneo di una decina di file.

Share this post


Link to post
Share on other sites

Il Tag è una proprietà del ListViewItem specifica dell'oggetto istanziato, quindi ogni elemento della lista avrà il suo Tag e così potrà avere associata una diversa coppia webClientHandler+webclient

 

Edit: Tutto questo vale se stiamo parlando di questo ListViewItem: System.Windows.Forms.ListViewItem

Edited by danielej

Share this post


Link to post
Share on other sites

Quindi, in pratica, devo solo associare l'oggetto webclient ad un tag, in questo modo una volta avviati i download, posso anche uscire dalla funzione Click del bottone senza rischiare di ritrovarmi con oggetti latenti che sarebbero cestinati dal garbage collector perchè tali oggetti sono sempre referenziati in un tag della lista..

 

boh, ci provo, è la prima volta che programmo in C# e non conosco bene .NET.

Anni fa avevo ottime conoscenze di Visual Basic, ma poi ho lasciato perdere, saran 7-8 anni che non programmo in VB e .net ancora non esisteva. (usavo visual studio 6)

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
Sign in to follow this  

×