Esercitazione: Combinare tecniche C# per generare etichette badge

Un meetup richiede etichette badge nell'ordine dei nomi. Ogni partecipante richiede una o più copie e ogni etichetta include un messaggio per il ruolo del partecipante. In questa esercitazione si combinano piccole tecniche C# per produrre le etichette senza una raccolta separata di stringhe formattate.

Questa esercitazione è destinata agli sviluppatori che conoscono metodi, cicli e raccolte C# di base. Si sceglie dove appartengono le operazioni brevi, si rendono esplicite le dipendenze helper e si decide quando il programma produce risultati. Per il ragionamento alla base di queste scelte, vedere Sviluppo del software.

Prerequisites

Iniziare con un'etichetta per partecipante

  1. Creare una cartella denominata BadgeLabels e un file denominato Program.cs al suo interno.

  2. Copiare il testo sorgente completo nella sezione seguente in Program.cs. Usare lo stesso file per ogni passaggio e esperimento:

    #define BADGE_SORT
    #define BADGE_COPIES
    #define BADGE_FORMAT
    #define BADGE_COUNT
    // #define BADGE_DIAGNOSTICS
    // #define BADGE_REPEAT
    // #define BADGE_SNAPSHOT
    
    Attendee[] attendees =
    [
        new("Ben", "Host", 1),
        new("Ada", "Guest", 2)
    ];
    
    #if BADGE_SORT
    #if BADGE_DIAGNOSTICS
    Console.WriteLine("Before query definition.");
    #endif
    var orderedAttendees = attendees.OrderBy(
    #if BADGE_DIAGNOSTICS
        attendee =>
        {
            Console.WriteLine($"Sort key: {attendee.Name}");
            return attendee.Name;
        }, StringComparer.Ordinal);
    #else
        attendee => attendee.Name, StringComparer.Ordinal);
    #endif
    #if BADGE_DIAGNOSTICS
    Console.WriteLine("After query definition.");
    #endif
    #else
    var orderedAttendees = attendees;
    #endif
    
    #if BADGE_SNAPSHOT
    var attendeesToPrint = orderedAttendees.ToList();
    #else
    var attendeesToPrint = orderedAttendees;
    #endif
    
    int passes = 1;
    #if BADGE_REPEAT
    passes = 2;
    #endif
    
    for (int pass = 0; pass < passes; pass++)
    {
    #if BADGE_COUNT
        int expected = attendees.Sum(attendee => attendee.Copies);
        int printed = 0;
    #endif
    
        foreach (Attendee attendee in attendeesToPrint)
        {
    #if BADGE_COPIES
            var labels = CreateLabels(attendee);
    #if BADGE_DIAGNOSTICS
            Console.WriteLine($"Sequence created: {attendee.Name}");
    #endif
            foreach (string label in labels)
            {
                Console.WriteLine(label);
    #if BADGE_COUNT
                printed++;
    #endif
            }
    #else
            Console.WriteLine($"{attendee.Name} | {attendee.Role}");
    #endif
        }
    
    #if BADGE_COUNT
        if (printed != expected)
        {
            throw new InvalidOperationException("Label count doesn't match the request.");
        }
    
        Console.WriteLine($"Printed {printed} of {expected} requested labels.");
    #endif
    }
    
    #if BADGE_COPIES
    static IEnumerable<string> CreateLabels(Attendee attendee)
    {
    #if BADGE_DIAGNOSTICS
        Console.WriteLine($"Iterator started: {attendee.Name}");
    #endif
        ArgumentOutOfRangeException.ThrowIfNegativeOrZero(attendee.Copies);
    
    #if BADGE_FORMAT
        string message = attendee.Role switch
        {
            "Guest" => "Welcome!",
            "Host" => "Ask me for help",
            _ => throw new ArgumentException(
                $"Unsupported role: {attendee.Role}.", nameof(attendee))
        };
    #endif
    
        for (int copy = 1; copy <= attendee.Copies; copy++)
        {
    #if BADGE_FORMAT
            yield return FormatLabel(
                attendee.Name, attendee.Role, message, copy, attendee.Copies);
    #else
            yield return $"{attendee.Name} | {attendee.Role} [{copy}/{attendee.Copies}]";
    #endif
        }
    
    #if BADGE_FORMAT
        static string FormatLabel(
            string name, string role, string message, int copy, int total) =>
            $"{name} | {role} | {message} [{copy}/{total}]";
    #endif
    }
    #endif
    
    record Attendee(string Name, string Role, int Copies);
    
  3. Nella parte superiore del file impostare come commento le quattro righe attive #define . Lasciare anche le tre righe dell'esperimento impostate come commento.

  4. Da tale cartella eseguire l'app basata su file:

    dotnet run --file Program.cs
    

Il programma scrive:

Ben | Host
Ada | Guest

Il file contiene ogni fase. Da ora in poi, modificare solo le #define righe nella parte superiore, non i corpi o le dichiarazioni dei metodi. Una #if direttiva include codice solo quando viene definito il relativo simbolo; un ramo #else fornisce l'alternativa. Vedi Compilazione condizionale.

Rimuovere il commento da una riga di fase aggiuntiva in ogni passaggio e mantenere attivate le linee di fase precedenti:

Stage Simboli della fase attiva
Un'etichetta per ogni partecipante None
Ordina i partecipanti BADGE_SORT
Crea copie BADGE_SORT, BADGE_COPIES
Formattare i messaggi BADGE_SORT, BADGE_COPIES, BADGE_FORMAT
Verifica il conteggio BADGE_SORT, BADGE_COPIES, BADGE_FORMATBADGE_COUNT

Il codice sorgente scaricato abilita tutti e quattro i simboli di fase, quindi esegue il programma finale per impostazione predefinita. Gli altri tre simboli controllano esperimenti facoltativi e rimangono disattivati durante la progressione normale. Questi simboli usano un prefisso BADGE_ per evitare simboli definiti dall’SDK come TRACE.

Gli estratti seguenti evidenziano il codice già presente nel file. Non copiarli sull'intero programma.

Attendee[] attendees =
[
    new("Ben", "Host", 1),
    new("Ada", "Guest", 2)
];

La Attendee dichiarazione definisce un record, un tipo che raggruppa i dati correlati e fornisce l'uguaglianza basata su valori. Ogni partecipante ha un Name, un Role e un numero richiesto di Copies. Un record si adatta a questo piccolo modello di dati perché il programma trasferisce i dati dei partecipanti tra operazioni senza modificarli. Per altre informazioni su questo tipo, vedere Record.

L'input fisso usa un array e un'espressione collection. In ogni new(...) chiamata, C# deduce Attendee dal tipo di elemento della matrice. Vedere Target-typed new. Le istruzioni di primo livello consentono di eseguire l'esempio senza scrivere un Main metodo.

Il foreach loop legge ogni partecipante e una stringa interpolata inserisce i relativi valori nell'output. Il ciclo attualmente ignora Copies.

Mantenere questo piccolo input durante lo sviluppo del programma: Ben richiede una copia e Ada richiede due. Solo Guest e Host sono ruoli supportati e i conteggi delle copie devono essere positivi.

Ordinare con un'espressione lambda

Per ordinare i partecipanti per nome, rimuovere il commento #define BADGE_SORT. Lasciare gli altri simboli commentati. Il ramo di ordinamento definisce questa query:

var orderedAttendees = attendees.OrderBy(
#if BADGE_DIAGNOSTICS
    attendee =>
    {
        Console.WriteLine($"Sort key: {attendee.Name}");
        return attendee.Name;
    }, StringComparer.Ordinal);
#else
    attendee => attendee.Name, StringComparer.Ordinal);
#endif

Con la diagnostica disattivata, il #else ramo fornisce l'espressione lambda breve. Il ciclo stampa i partecipanti ordinati:

    foreach (Attendee attendee in attendeesToPrint)
    {
#if BADGE_COPIES
        var labels = CreateLabels(attendee);
#if BADGE_DIAGNOSTICS
        Console.WriteLine($"Sequence created: {attendee.Name}");
#endif
        foreach (string label in labels)
        {
            Console.WriteLine(label);
#if BADGE_COUNT
            printed++;
#endif
        }
#else
        Console.WriteLine($"{attendee.Name} | {attendee.Role}");
#endif
    }

Eseguire lo stesso comando. L'output ora mette Ada per prima:

Ada | Guest
Ben | Host

attendee => attendee.Name è un'espressione lambda, una funzione senza nome fornita dove il codice richiede un comportamento. In questo caso, l'espressione lambda seleziona il nome in base al quale eseguire l'ordinamento. L'espressione lambda rimane inline, direttamente nella OrderBy chiamata, perché è breve e lo scopo è chiaro. È possibile estrarre la selezione in un metodo helper denominato, ma un nome di metodo non spiega meglio questa selezione basata su una sola proprietà. Vedi Espressioni lambda.

OrderByfa parte di LINQ (Language Integrated Query), un set di funzionalità C# e API .NET per le query sui dati. Vedere le query LINQ.

In questo esempio viene utilizzata la sintassi del metodo, con cui vengono chiamati direttamente i metodi LINQ. Al contrario, la sintassi di query usa clausole C# come from, orderbye select:

var orderedAttendees =
    from attendee in attendees
    orderby attendee.Name
    select attendee;

Il compilatore converte la sintassi di query in chiamate al metodo. La sintassi delle query può semplificare la lettura di una query con diverse clausole; la sintassi del metodo mantiene questo singolo ordinamento breve e consente di passare un comparatore a OrderBy. L'esempio di sintassi di query usa l'operatore di confronto di stringhe predefinito, quindi non garantisce lo stesso ordine tra le impostazioni cultura. Mantenere la versione con sintassi di metodo per questo programma. Vedi metodo LINQ e sintassi di query.

Per mantenere l'output indipendente dalle impostazioni cultura del computer, l'esempio usa Ordinal per i confronti dei nomi. Per i nomi brevi, l'ordine è alfabetico. Vedere Ordinare i dati con LINQ.

La var dichiarazione consente al compilatore di dedurre il tipo della variabile di query. La orderedAttendees variabile descrive una query, non una raccolta ordinata completata. Enumerare una sequenza significa leggere i suoi elementi uno alla volta. OrderBy legge e ordina tutti gli elementi quando foreach richiede per la prima volta un elemento.

Osservare l'ordine di esecuzione

L'esecuzione posticipata indica che il lavoro inizia quando il codice enumera una sequenza anziché quando il codice definisce la query. Vedere Eseguire una query. Usare l'output di diagnostica per visualizzare la differenza:

  1. Mantieni abilitato solo BADGE_SORT tra i simboli di fase.

  2. Rimuovere il commento #define BADGE_DIAGNOSTICS nella parte superiore dello stesso file. I rami di diagnostica circondano la definizione di query con messaggi e usano un'espressione lambda che stampa ogni chiave di ordinamento:

    #if BADGE_SORT
    #if BADGE_DIAGNOSTICS
    Console.WriteLine("Before query definition.");
    #endif
    var orderedAttendees = attendees.OrderBy(
    #if BADGE_DIAGNOSTICS
        attendee =>
        {
            Console.WriteLine($"Sort key: {attendee.Name}");
            return attendee.Name;
        }, StringComparer.Ordinal);
    #else
        attendee => attendee.Name, StringComparer.Ordinal);
    #endif
    #if BADGE_DIAGNOSTICS
    Console.WriteLine("After query definition.");
    #endif
    #else
    var orderedAttendees = attendees;
    #endif
    
  3. Eseguire dotnet run --file Program.cs di nuovo. Prima di leggere l'output, prevedere se Sort key: viene visualizzato prima o dopo After query definition..

L'output è il seguente:

Before query definition.
After query definition.
Sort key: Ben
Sort key: Ada
Ada | Guest
Ben | Host

Nessuna chiave di ordinamento compare tra i due messaggi di definizione di query. La lambda viene eseguita solo quando foreach richiede il primo risultato. Entrambi i messaggi chiave vengono visualizzati prima della prima etichetta perché OrderBy ha bisogno di tutte le chiavi prima di poter determinare quale partecipante viene per primo. Le chiavi seguono l'ordine di input, Ben quindi Ada; i risultati seguono l'ordine ordinato, Ada e poi Ben.

L'espressione lambda di diagnostica richiede sia un messaggio che una chiave restituita, quindi usa un corpo di istruzione a blocco con parentesi graffe e return, anziché il corpo originale con un'espressione singola.

Con la diagnostica attivata, provare le due opzioni dell'esperimento nello stesso file. Non sono necessarie modifiche ai cicli o alle query:

  1. Rimuovere il commento #define BADGE_REPEAT ed eseguire di nuovo:

    Before query definition.
    After query definition.
    Sort key: Ben
    Sort key: Ada
    Ada | Guest
    Ben | Host
    Sort key: Ben
    Sort key: Ada
    Ada | Guest
    Ben | Host
    

    Il secondo passaggio ripete l'ordinamento. La variabile di query non memorizza nella cache i risultati.

  2. Mantenere BADGE_REPEAT abilitato, decommentare #define BADGE_SNAPSHOT ed eseguire di nuovo:

    Before query definition.
    After query definition.
    Sort key: Ben
    Sort key: Ada
    Ada | Guest
    Ben | Host
    Ada | Guest
    Ben | Host
    

    I due messaggi chiave vengono ora visualizzati una sola volta, durante ToList, prima che uno dei due pass stampi le etichette. Entrambi i pass leggono i partecipanti archiviati.

Queste opzioni selezionano la sequenza da enumerare e il numero di passate:

#if BADGE_SNAPSHOT
var attendeesToPrint = orderedAttendees.ToList();
#else
var attendeesToPrint = orderedAttendees;
#endif

int passes = 1;
#if BADGE_REPEAT
passes = 2;
#endif

L'esecuzione eager indica che un'operazione viene eseguita immediatamente anziché attendere l'enumerazione successiva. ToList esegue la query immediatamente e ne archivia i risultati in un elenco. Entrambi i passaggi ora leggono i partecipanti archiviati senza eseguire di nuovo l'ordinamento. Al contrario, la definizione della OrderBy query rinvia ancora l'ordinamento: legge e memorizza nel buffer tutti gli input solo all'inizio dell'enumerazione. Il buffering non significa che la query venga eseguita quando viene definita. Usare uno snapshot quando è necessario riutilizzare i risultati senza ricalcolare i risultati; mantenere la query posticipata quando si desidera che ogni enumerazione legga l'input corrente. Vedere Comporre query con risultati memorizzati nella cache.

Prima di continuare, commenta di nuovo BADGE_DIAGNOSTICS, BADGE_REPEAT e BADGE_SNAPSHOT. Mantieni BADGE_SORT abilitato. Eseguire l'app per verificare che vengano visualizzate solo le due etichette ordinate.

Produrre le copie richieste con un fascicolatore

Per espandere ogni partecipante nel numero di etichette richiesto, rimuovere il commento #define BADGE_COPIES insieme a BADGE_SORT. Il ciclo seleziona ora il BADGE_COPIES ramo illustrato in precedenza e chiama l'iteratore già nel file:

#if BADGE_COPIES
static IEnumerable<string> CreateLabels(Attendee attendee)
{
#if BADGE_DIAGNOSTICS
    Console.WriteLine($"Iterator started: {attendee.Name}");
#endif
    ArgumentOutOfRangeException.ThrowIfNegativeOrZero(attendee.Copies);

#if BADGE_FORMAT
    string message = attendee.Role switch
    {
        "Guest" => "Welcome!",
        "Host" => "Ask me for help",
        _ => throw new ArgumentException(
            $"Unsupported role: {attendee.Role}.", nameof(attendee))
    };
#endif

    for (int copy = 1; copy <= attendee.Copies; copy++)
    {
#if BADGE_FORMAT
        yield return FormatLabel(
            attendee.Name, attendee.Role, message, copy, attendee.Copies);
#else
        yield return $"{attendee.Name} | {attendee.Role} [{copy}/{attendee.Copies}]";
#endif
    }

#if BADGE_FORMAT
    static string FormatLabel(
        string name, string role, string message, int copy, int total) =>
        $"{name} | {role} | {message} [{copy}/{total}]";
#endif
}
#endif

Eseguire di nuovo l'app:

Ada | Guest [1/2]
Ada | Guest [2/2]
Ben | Host [1/1]

CreateLabels è un iteratore, una funzione che produce elementi di sequenza uno alla volta. Il IEnumerable<T> tipo restituito, scritto come IEnumerable<string>, rappresenta una sequenza di stringhe. Ognuno yield return fornisce un'etichetta al ciclo interno foreach, che la stampa prima di richiedere l'etichetta successiva. Vedi Iteratori.

Il for ciclo conta le copie da 1 a attendee.Copies. Un partecipante produce tante stringhe quante richieste Copies. Una raccolta funziona anche, ma il chiamante necessita solo di ogni etichetta una sola volta. L'iteratore evita una raccolta di etichette separata e mantiene il ciclo copy-number insieme. I cicli esterni e interni foreach separano l'ordine degli iscritti dalla produzione di etichette. Il chiamante rimane responsabile della stampa.

Chiamare CreateLabels(attendee) non esegue il corpo. Analogamente alla query LINQ, l'iteratore usa l'esecuzione posticipata. Il comando interno foreach avvia il lavoro, incluso il controllo della copia positiva. Una nuova enumerazione itera nuovamente l'iteratore. A differenza di OrderBy, che legge e ordina tutti i partecipanti prima del primo risultato, questo iteratore fornisce un'etichetta senza creare le etichette rimanenti.

Per osservare l'ordine di esecuzione dell'iteratore, rimuovere nuovamente il commento #define BADGE_DIAGNOSTICS . Mantieni BADGE_REPEAT e BADGE_SNAPSHOT disattivati. La diagnostica mostra ora sia l'ordinamento che l'iteratore:

Before query definition.
After query definition.
Sort key: Ben
Sort key: Ada
Sequence created: Ada
Iterator started: Ada
Ada | Guest [1/2]
Ada | Guest [2/2]
Sequence created: Ben
Iterator started: Ben
Ben | Host [1/1]

Per ogni partecipante, il messaggio creato dalla sequenza viene visualizzato prima del messaggio avviato dall'iteratore e delle etichette. La creazione della sequenza non immette nel corpo dell'iteratore; la richiesta del primo elemento lo fa. Commenta di nuovo BADGE_DIAGNOSTICS prima di continuare.

Gli stessi controlli dell'esperimento rimangono disponibili nelle fasi successive. BADGE_REPEAT enumera i partecipanti due volte e crea di nuovo la sequenza di etichette di ogni partecipante. BADGE_SNAPSHOT memorizza nella cache solo i partecipanti ordinati, non le stringhe di etichetta, quindi impedisce un altro ordinamento, ma non impedisce l'esecuzione dell'iteratore a ogni iterazione.

Il ThrowIfNegativeOrZero controllo segnala l'input non valido anziché non produrre etichette in modo silenzioso. Un'eccezione segnala un errore che interrompe la normale esecuzione; vedere Creare e generare eccezioni. In questo caso, un conteggio di copie non valido arresta il programma invece di ignorare automaticamente il partecipante.

Calcolare un messaggio e assegnare un nome al passaggio di formattazione

Ogni ruolo richiede un messaggio diverso. Rimuovere il commento #define BADGE_FORMAT, e lasciare BADGE_COPIES e BADGE_SORT abilitati. Lo stesso iteratore include ora il calcolo del messaggio:

#if BADGE_FORMAT
    string message = attendee.Role switch
    {
        "Guest" => "Welcome!",
        "Host" => "Ask me for help",
        _ => throw new ArgumentException(
            $"Unsupported role: {attendee.Role}.", nameof(attendee))
    };
#endif

Seleziona anche il yield return FormatLabel(...) ramo e include questo helper locale:

#if BADGE_FORMAT
    static string FormatLabel(
        string name, string role, string message, int copy, int total) =>
        $"{name} | {role} | {message} [{copy}/{total}]";
#endif

Eseguire di nuovo l'app:

Ada | Guest | Welcome! [1/2]
Ada | Guest | Welcome! [2/2]
Ben | Host | Ask me for help [1/1]

L'espressioneswitch calcola il messaggio una volta per partecipante, prima del ciclo di copia. Un'espressione si adatta perché ogni ruolo seleziona un valore da assegnare a message. Una if/else catena o un'istruzione switch funzionerebbe, ma ripeterebbe l'assegnazione in ogni ramo.

Corrispondenza di pattern testa un'espressione rispetto a un pattern specificato. Qui, attendee.Role è l'espressione di input e i modelli costanti"Guest" e "Host" ne testano il valore valutato per selezionare il messaggio corrispondente. Il pattern discard _ corrisponde a qualsiasi altro ruolo e genera un'eccezione, quindi i ruoli non supportati non ricevono un messaggio predefinito fuorviante. Vedi corrispondenza dei modelli.

L'operatorenameof fornisce all'eccezione il nome del parametro attendee. A differenza di una stringa hardcoded, tale nome rimane corretto se si rinomina il parametro con uno strumento di refactoring.

FormatLabel è una funzione locale, una funzione denominata dichiarata all'interno di un'altra funzione. Poiché solo CreateLabels necessita dell'operazione di formattazione, la sua collocazione all'interno di CreateLabels limita l'helper all'operazione che lo usa. Un metodo membro privato in una classe renderebbe disponibile l'helper ad altri membri di tale classe. Scegliere l'ambito più ampio se più operazioni richiedono la stessa operazione di formattazione. Vedi Funzioni locali.

Il nome FormatLabel consente di seguire il ciclo di copia senza leggere i dettagli di formattazione. Anche mantenere la stringa interpolata in yield return funzionerebbe, come nel passaggio precedente. Il nome diventa più utile ora che un'etichetta include un messaggio specifico del ruolo. Il corpo dell'espressione dell'helper => restituisce la stringa formattata.

Tutti gli input di formattazione arrivano tramite parametri. Una funzione locale non statica può acquisire o usare variabili dall'ambito circostante. Il static modificatore impedisce l'acquisizione, quindi le relative dipendenze rimangono esplicite. Vedere Parametri e stato acquisito.

Poiché CreateLabels è ancora un iteratore, il controllo del ruolo viene eseguito solo all'avvio dell'enumerazione. Per questo input controllato, questi controlli appartengono alla produzione delle etichette. Il programma non promette di convalidare tutti i partecipanti prima di stampare qualsiasi etichetta.

Verificare il totale ed eseguire il programma completo

Per confrontare l'effettiva produzione di etichette con il conteggio richiesto, rimuovere il commento #define BADGE_COUNT. Tutti e quattro i simboli di fase sono ora abilitati. Lasciare i tre simboli dell'esperimento commentati per l'output finale normale.

La configurazione del conteggio viene eseguita prima del ciclo dei partecipanti:

#if BADGE_COUNT
    int expected = attendees.Sum(attendee => attendee.Copies);
    int printed = 0;
#endif

Sum usa l'esecuzione eager per aggiungere i conteggi di copia dalla matrice di input. A differenza di OrderBy, restituisce un numero, non una sequenza da enumerare in un secondo momento. L'espressione lambda, attendee => attendee.Copies, seleziona il conteggio delle copie inline. Come per la selezione del nome, un metodo helper separato non chiarirebbe questa breve espressione. Un ciclo con un accumulatore funzionerebbe anche; Sum indica direttamente che si desidera un totale. Vedere Metodi LINQ comuni.

L'printed++incremento nel loop con etichetta aumenta il contatore solo quando il loop riceve un'etichetta. Dopo questo ciclo, l'istruzioneif confronta il conteggio con il totale richiesto e genera un'eccezione se differiscono:

#if BADGE_COUNT
    if (printed != expected)
    {
        throw new InvalidOperationException("Label count doesn't match the request.");
    }

    Console.WriteLine($"Printed {printed} of {expected} requested labels.");
#endif

Hai raggiunto la configurazione predefinita del programma completo copiato all'inizio. Nessun metodo o dichiarazione da modificare:

#define BADGE_SORT
#define BADGE_COPIES
#define BADGE_FORMAT
#define BADGE_COUNT
// #define BADGE_DIAGNOSTICS
// #define BADGE_REPEAT
// #define BADGE_SNAPSHOT

Eseguilo con dotnet run --file Program.cs. L'output è il seguente:

Ada | Guest | Welcome! [1/2]
Ada | Guest | Welcome! [2/2]
Ben | Host | Ask me for help [1/1]
Printed 3 of 3 requested labels.

L’array di input è già in memoria. Sum lo legge immediatamente, OrderBy lo ordina quando inizia il ciclo dei partecipanti e CreateLabels produce stringhe man mano che il ciclo interno avanza. Il programma non crea mai una raccolta separata di etichette. Per indicazioni più generali sulle query, vedere LINQ e raccolte.

Per rivedere la diagnostica con il programma finale, abilitare BADGE_DIAGNOSTICS da solo tra i simboli dell'esperimento. L'output è il seguente:

Before query definition.
After query definition.
Sort key: Ben
Sort key: Ada
Sequence created: Ada
Iterator started: Ada
Ada | Guest | Welcome! [1/2]
Ada | Guest | Welcome! [2/2]
Sequence created: Ben
Iterator started: Ben
Ben | Host | Ask me for help [1/1]
Printed 3 of 3 requested labels.

Se si abilita BADGE_REPEATanche , ogni passaggio verifica e segnala il proprio conteggio. Con BADGE_SNAPSHOT abilitato, le chiavi di ordinamento vengono visualizzate una sola volta, ma i messaggi dell'iteratore, le etichette e il report del conteggio vengono comunque visualizzati a ogni passaggio. Mettere in commento i simboli dell'esperimento per ripristinare l'output finale normale.

Summary

Ogni tecnica serve una parte diversa della stessa attività:

  • Le espressioni lambda e LINQ selezionano i nomi e i conteggi delle copie inline per l'ordinamento e il totale.
  • Un iteratore esprime la produzione uno-a-molti personalizzata e lascia il consumo di etichette al chiamante.
  • Un'switchespressione calcola un valore specifico del ruolo e rende espliciti i ruoli non supportati.
  • Una funzione locale assegna un nome al passaggio di formattazione, limita l'ambito e ne indica gli input.

Scegliere questi formulari quando l'ambito, le dipendenze e l'ordine di esecuzione corrispondono all'operazione. Un ciclo diretto, un metodo condiviso o una raccolta completata possono adattarsi meglio ad altri chiamanti.