131 messaggi dal 12 febbraio 2001
Salve,

vorrei sapere se c'e' qualche modo per rilevare un operazione di enumerazione su una Collection.
Esempio:

Ho una mia collection personalizzata che eredita da Collection<T>. Vorrei poter sapere quando viene chiamato foreach su questa collection.

Grazie

Edika
Il metodo di enumerazione è GetEnumerator (richiesto dall'interfaccia IEnumerable)

Matteo Casati
GURU4.net
Ciao,

prova così:
public class MyCustomCollection<T> : Collection<T>, IEnumerable<T>, IEnumerable
{
    public new IEnumerator<T> GetEnumerator()
    {
        Console.WriteLine("Enumerating");
        return base.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

m.
131 messaggi dal 12 febbraio 2001
Grazie,

purtroppo non è come mi aspettavo.
La necessita di sapere se è in corso un enumerazione è per evitare di fare modifiche alla collection durante questa operazione ed accodarle da qualche aprte ed eseguirle alla fien dell'enumerazione.
Sono arrivato a questa soluzione perche non è disponibile la proprieta SyncRoot per Collection<T> e quindi non riesco a fare il lock dell'insieme.
Quello che mi aspettavo, o volevo riuscire ad implementare erano due eventi, uno per l'inizio e uno per la fine dell'enumerazione.

Ciao

Edika
No, allora dovevi spiegare il tuo problema con maggiore chiarezza

Non è quella la strada da percorrere perché riesci ad intercettare la creazione dell'enumeratore ma non il suo utilizzo. Se sei sul framework 4.0 puoi utilizzare le collection di System.Collections.Concurrent che sono threadsafe e supportano enumerazioni in ambiti multithreading.

Diversamente, io fossi in te intercetterei la richiesta di un Enumerator come ti ho mostrato, per creare uno snapshot della collection in quell'istante. Quindi
1) lock
2) crei una collection inizializzandola con gli elementi della tua collezione
3) restituisci l'enumerator della collection del punto 2
4) release del lock

Fammi sapere

Ciao,
m.
131 messaggi dal 12 febbraio 2001
Ciao,

scusa se non sono stato chiaro, ma avevo ridotto al minimo la richiesta perche il problema è un po piu vasto, in quanto si tratta non di una semplice Collection<T> ma di un oggetto business di un ORM che include molte altre funzionalita.
Il problema nasce infatti dalla richiesta di salvataggio di un oggetto della collection durante l'enumerazione. Se l'oggetto è contrassegnato per la cancellazione, al momento del salvataggio, la collection lo elimina dalla lista, provocando un eccezione in quanto c'e' un enumerazione in corso.
Esempio:
foreach(BusinessObject bo in BusinessObjectCollection)
{
bo.Delete();
bo.Save();
}

La metodologia corretta sarebbe quella di segnare l'oggetto per la cancellazione e quindi chiamare il metodo Save sulla collection, in questo modo il lock sono in grado di gestirlo e gli oggetti li rimuovo dalla collection al momento giusto.

foreach(BusinessObject bo in BusinessObjectCollection)
{
bo.Delete();
}
BusinessObjectCollection.Save();

Volevo pero evitare questa limitazione all'utilizzatore dell'ORM fornendo almeno un metodo SyncRoot gestito da me, visto che Collection<T> non lo mette a disposizione (perche poi...?!).

Spero adesso che la problematica sia piu chiara.

Ciao

Edika
Modificato da edika il 24 giugno 2010 11.51 -
Sì, molto più chiara e in generale secondo me c'è una problematica più di design a questo punto.

Posso chiederti come mai ti trovi ad avere la tua entità di dominio acceduta da più thread?

Ciao,
m.
131 messaggi dal 12 febbraio 2001
L'eventualita che l'entita venga usata da piu thread dipende da come viene svilupata l'applicazione che utilizza l'ORM.
La cosa che volevo gestire era la situazione che ho spiegato nel primo esempio, evitando che si verificasse l'eccezione durante l'enumerazione.
La collection sottoscrive l'evento OnSaved ad ogni aggiunta di un oggetto, e gestisce l'evento in questo modo.

void item_Saved(object sender, SavedEventArgs e)
{
T item = sender as T;
if (e.Saved && e.SaveType == SavingType.Delete)
{
Remove(item);
OnCollectionChanged(NotifyCollectionChangedAction.Remove);
}
}

Mettendo a disposizione un oggetto SyncRoot in questo modo

private readonly object _syncroot = new object();

public object SyncRoot
{
get
{
return _syncroot;
}
}

ed utilizzandolo

lock(SyncRoot)
{
foreach(BusinessObject bo in BusinessObjectCollection)
{
bo.Delete();
bo.Save();
}
}

l'eccezione non viene invocata e l'oggetto viene rimosso alla fine dell'enumerazione corretto?

Grazie

Edika
Modificato da edika il 25 giugno 2010 10.29 -

Torna al forum | Feed RSS

ASPItalia.com non è responsabile per il contenuto dei messaggi presenti su questo servizio, non avendo nessun controllo sui messaggi postati nei propri forum, che rappresentano l'espressione del pensiero degli autori.