Evitare chiusure

2

Ho una struttura dati sotto forma di albero. Ogni nodo rappresenta una scatola di cartone. Le caselle possono contenere caselle figlio.

public class CardboardBox
{
   public int Id {get; set;}
   public int ParentBoxId {get; set;}

   // [...]
}

Sto memorizzando quegli oggetti in una lista piatta piuttosto che in una struttura ad albero:

public class CardboardBoxList : List<CardboardBox>
{
}

Voglio poter attraversare l'albero così ho creato un "per ogni" delgate come questo:

public class CardboardBox
{
   // [...]

   public delegate void ForEachDelegate(int childId);

   public void ForEachChildBox(CardboardBoxList fullListOfBoxes, ForEachDelegate forEachDelegate)
   {
      List childIdList = fullListOfBoxes.GetChildIdList(this.Id);

      foreach(int childId in childIdList)
      {
         forEachDelegate(childId);
      }
   }
}

Questo è tutto buono perché ora posso facilmente recurse attraverso il box-tree con:

someBox.ForEachChild(fullListOfBoxes, childId =>
{
   CardboardBox currentBox = fullListOfBoxes.GetById(childId);

   // [... do something with it ...]
};

Il problema è che ho il codice che vuole passare attraverso le caselle e costruire un'altra struttura dati. E ho bisogno di avere alcune variabili al di fuori della chiamata del delegato in questo modo:

NewDataStructure newDataStructure = new NewDataStructure();

int currentParentNodeId = newDataStructure.AddRootNode();

someBox.ForEachChild(fullListOfBoxes, childId =>
{
   CardboardBox currentBox = fullListOfBoxes.GetById(childId);

   NewDataStructureNode newNode = new NewDataStructureNode();
   newNode.Color = currentBox.Color;

   newDataStructure.AddNodeToParent(newNode, currentParentNodeId);
};

Quindi ha bisogno di creare una chiusura per currentParentNodeId mentre costruisce la nuova struttura ad albero. Ma voglio provare a evitare l'uso delle chiusure, se possibile, solo per mantenere il codice semplice.

Mi rendo conto che le chiusure arrivano solo con il territorio quando si usano i delegati. In un certo senso è un po 'il punto di usare i delegati. Vuoi avere un metodo anonimo che interagisca con il codice che lo circonda, piuttosto che dover definire un metodo completamente nuovo come i vecchi tempi.

Mi chiedo solo se c'è un modo migliore qui. La struttura dei dati dell'albero di CardboardBox verrà ampiamente utilizzata in questo codice e talvolta richiederà la chiusura di tutti i tipi di variabili diverse, pertanto non è possibile aggiungere un nuovo parametro al tipo di delegato. Avrei bisogno di creare un nuovo delegato e un nuovo metodo ForEachChild per ogni tipo di delegato.

Potrei provare ad aggiungere un parametro generico al delegato e passare semplicemente una sorta di parametro di contesto, ma ancora non sembra pulito. Alla fine sto cercando di evitare di avere delle chiusure se possibile, ma non sono sicuro che sto provando a farlo.

Si noti che questo codice è tutto parafrasato e l'esempio (probabilmente zoppo) di CardboardBox è in realtà un tipo di oggetto contenitore nel codice reale. NewDataStructure è anche un albero nel codice reale ma non ha necessariamente la stessa struttura dell'albero da cui è stato costruito.

Ho postato questa domanda su StackOverflow ieri ma la domanda è stata ritenuta troppo vaga, comprensibilmente. Ho provato a riscriverlo più chiaramente, ma ammetto che è ancora abbastanza poco chiaro. Ho anche fatto un bel po 'di ricerche negli ultimi giorni e ho letto i delegati, i metodi anonimi e le chiusure che sono state utili. Ma ancora non mi sento a mio agio usando solo le chiusure, volenti o nolenti. Quindi apprezzerei qualsiasi consiglio o solo opinioni su tutto questo.

Dichiarazione di non responsabilità: ho usato c # per molti anni quindi se ci sono errori di sintassi non ho bisogno che tu li segnali. Sto parafrasando il vero codice dalla mia testa.

    
posta LegendLength 05.02.2015 - 15:59
fonte

1 risposta

3

Perché non progettare l'interfaccia di CardBoardBox utilizzando un IEnumerable :

public class CardboardBox
{
   // [...]

   public IEnumerable<int> GetChildBoxIDs(CardboardBoxList fullListOfBoxes)
   {
      List childIdList = fullListOfBoxes.GetChildIdList(this.Id);

      foreach(int childId in childIdList)
      {
         yield return childId;
      }
      // remark: this function could be arbitrarily more complex
   }
}

Nel tuo esempio, il codice chiamante appare in questo modo:

foreach(var childId in someBox.GetChildBoxIDs(fullListOfBoxes))
{
    CardboardBox currentBox = fullListOfBoxes.GetById(childId);

  // [... do something with it ...]
}

Quindi il chiamante non ha più bisogno di fornire una funzione anonima.

    
risposta data 05.02.2015 - 16:16
fonte

Leggi altre domande sui tag