Elementi ordinati in modo esplicito o esplicito?

1

Quindi, ho il seguente problema:

Ho un numero di elementi ordinati, ordinati nel modo seguente:

  • Tipo 1: deve essere il primo elemento. (solo uno possibile, sempre presente)
  • Tipo 2: deve essere il secondo e i seguenti elementi, se presenti.
  • Tipo 3: deve essere l'ultimo ordinato.

Ha dichiarato un po 'più succintamente / pseudoregex:

12*3+

Ora, quando costruisci questi elementi, lo faccio in un metodo, dove è facile fare qualcosa del tipo:

public List<IElement> CreateElements(...)
{
    var list = ...;
    list.Add(new Type1());
    list.AddRange(GetType2Elements());
    list.AddRange(GetType3Elements());
}

Tuttavia, non esiste un ordine semantico esplicito - è solo una lista, che mi capita di costruire in un certo modo.

Un modo più esplicito è fare in modo che IElement implementino IComparable, quindi utilizzare un elenco ordinato in modo esplicito e restituirlo. Ciò porterebbe il "ordinamento" al di fuori del mio metodo di costruzione e ordinerebbe le entità indipendentemente da come sono state create.

Tuttavia, è improvvisamente un po 'meno banale, dal momento che almeno 3 diversi metodi di confronto devono essere implementati.

Che cosa sceglieresti?

    
posta Max 10.12.2011 - 11:21
fonte

3 risposte

1

Lo risolverei in questo modo:

// returns a list of elements, ordered by type 1, type 2 and type 3
public List<IElement> CreateElementsOrdered(...)
{
    ...
}

Poiché il nome e il commento della tua funzione definiscono il contratto , ogni consumatore del tuo metodo può fare affidamento sul fatto che la lista è ordinata in questo modo. D'altra parte, chiunque modifichi CreateElementsOrdered ora saprà che la modifica dell'ordine degli elementi restituiti (in un modo che interrompe l'ordine 1 - 2 - 3) interromperà la compatibilità all'indietro.

    
risposta data 10.12.2011 - 11:46
fonte
1

Una classe semplice non sarebbe meglio di una lista? Puoi creare una classe che contiene i tre elementi e quindi un metodo costruttore / fabbrica per crearla nell'ordine richiesto.

Ad esempio:

public class Wrapper
{
    public Type1 MainObject { get; set; }
    public List<Type2> SubElements { get; set; }
    public List<Type3> OtherElements { get; set; }
}

public Wrapper() { }

public static Wrapper FactoryCreateOrdered()
{
    return new Wrapper
    {
        MainObject = new Type1();
        SubElements = //null if not present or a list of Type2s
        OtherElements = GetType3Elements();
    };
}

Questa è una struttura molto più ovvia di quella di un elenco che potrebbe avere un valore nullo in posizione 2 ma dati nelle posizioni 3 in poi. Mettendo questo in un elenco si richiedono eccezioni nella produzione perché ci sarà un percorso di codice che è usato raramente, non ha grandi / nessun test unitario e lo sviluppatore non si aspettava che un elemento del suo elenco fosse nullo.

Modifica: mi sono un po 'distratto dalla domanda riguardante l'ordinamento, ma ora che hai una classe personalizzata, puoi applicare qualsiasi criterio di ordinamento che desideri e sarà miglia più facile di cercare di fondere un elenco per funzionare per te.

    
risposta data 10.12.2011 - 12:54
fonte
0

Dato che hai scritto nel tuo commento che hai principalmente bisogno dell'elenco per un ciclo foreach , lasciatemi suggerire un approccio alternativo. Perché non restituire un IEnumerable (immutabile) anziché un elenco (mutabile)?

public IEnumerable<IElement> CreateElements()
{
    yield return new Type1();
    foreach (var e in GetType2Elements())
        yield return e;
    foreach (var e in GetType3Elements())
        yield return e;
}

Poiché l'oggetto IEnumerable creato da yield è immutabile, l'ordine non può mai cambiare. Tuttavia, sarebbe ancora possibile fare

foreach (var e in CreateElements())
    e.consume();

Nota, tuttavia, che il comportamento cambia leggermente a causa dell'esecuzione posticipata:

var elements = CreateElements();  // Elements have not been created yet!
foreach (var e in elements)       // New Type1 is executed as soon as the first
    e.consume();                  // element is accessed.

Se utilizzi il wrapper di stu , puoi aggiungere un enumeratore personalizzato come segue:

public class Wrapper : IEnumerable<IElement>
{ 
    ....

    public IEnumerator<IElement> GetEnumerator()
    {
        yield return MainObject;
        foreach (Type2 e in SubElements)
            yield return e;
        foreach (Type3 e in OtherElements)
            yield return e;

    } 

    // required since IEnumerable<T> inherits non-generic IEnumerable
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

Questo ti permetterebbe di fare

foreach (var e in myWrapper)
    e.consume();

Poiché myWrapper non espone mai la sua enumerazione come elenco, non può mai essere riordinato.

    
risposta data 10.12.2011 - 14:33
fonte

Leggi altre domande sui tag