2016-05-30 17 views
10

Attualmente ho la sfortuna di lavorare sul codice C# di Somebody Else che mi ha davvero fatto impazzire. Non ho idea di come la persona prima di me abbia mai mantenuto questo codice, poiché le sue varie patologie hanno bloccato l'IDE, il compilatore, l'ambiente di runtime ...Pulizia nidificata "if {} else {if {} else {if {...}}}"

Il problema che sto affrontando oggi riguarda un file sorgente di 15 megabyte che dispone di un grado di annidamento patologico veramente sconvolgente. Codice come:

if(var == 0) { 
    // do stuff 
} 
else { 
    if(var == 1) { 
    // do stuff 
    } 
    else { 
    if(var == 2) { 
     // do stuff, identical word for word to the `var == 1` case 
    } 
    else { 
     // etc. 
    } 
    } 
} 

Questa è una scelta stilistica discutibile nel migliore dei casi. Tuttavia, questo si combina con un'altra patologia del codice: alcuni di questi blocchi sono quasi un migliaio di livelli profondi. (La cosa che mi preoccupai di misurare più a fondo era di oltre 700.) Spero sinceramente che la persona prima di me, come uno dei loro atti finali prima di essere forzatamente separata da questo codice, abbia eseguito uno strumento di progettazione che ha provocato l'abominio davanti a me. Non riesco a immaginare che possano aver scritto questo codice come lo è ora, soprattutto dal momento che ogni terza o quarta modifica al codice arresta l'IDE. (E a volte cancella la mia copia del file sorgente, come bonus.)

Ho scritto un semplice strumento basato su espressioni regolari per cercare di condensare i casi più semplici, ma sembra a metà processo e quindi danneggiare questo particolare codice. (Non sono sicuro se fallisce perché questo codice usa di tanto in tanto condizionali per il preprocessore, o perché il più lungo degli incontri sarebbe lungo quasi 10MB e l'abbinatore di espressioni regolari di Lua non può farcela.) Spero ci sia uno strumento o una tecnica ampiamente utilizzata che può invertire questo problema. Ho già dovuto usare astyle per ripulire alcuni "problemi" stilistici del codice. L'opzione --remove-brackets per lo strumento quasi fa ciò che voglio, ma richiede che l'istruzione tra parentesi sia una singola istruzione su una singola riga, il che non è molto il caso qui ... (E solo per incrociare le mie "t", ho controllato; astyle non ha creato questo particolare problema)

Edit: approfondimento del codice problema rivela le cose in questo modo:

#if OneThing 
int num2296 = otherThing(); 
#endif 
#if AnotherThing 
int num44 = otherThing() 
int num45 = 0; 
#endif 
int num72 = 0; 
#if OneThing 
int num45 = 0; // note: multiple equivalent declarations of num45 
#endif 
#if OneThing 
for(int num2297 = 0; num2297 < num2296; ++num2297) { 
    num45 = doSomething(num2297); 
#endif 
#if AnotherThing 
for(int num43 = 0; num43 < num44; ++num43) { 
    num45 = doSomething(num43); 
#endif 
    if(somethingElse(num45)) { 
    ++num72; 
    } 
} // note: only one closing brace for the two protected by #ifs 

Due versioni di questo codice sono compilati per scopi diversi, uno con onething. definito e uno con AnotherThing definito. Tuttavia, la maggior parte delle differenze tra i due sono solo nomi variabili, con logica identica. (La maggior parte, non tutte.)

Casi come il tutore alla fine del frammento di cui sopra spiega perché il mio semplice strumento si stava rompendo. Questo aspetto appare sempre più simile alla sicurezza del lavoro in base alla progettazione e meno alla incompetenza innocente. (Se il codice è stato una volta in un punto in cui un nome variabile come num2276 sarebbe generato da un decompilatore, non è attualmente in quel punto.)

Sfortunatamente, ciò significa uno strumento automatico probabilmente non tagliato solo. Dovrò solo sfondare, annullando lentamente il danno che ha fatto l'ultimo programmatore. Lascio questa domanda qui nella remota possibilità che ci sia uno strumento miracoloso che, non so, può convertire entrambe le versioni in SSA e identificare e comprimere le loro equivalenze logiche e quindi riconvertirle ...

+0

caso di commutazione può essere una soluzione o [catena di responsabilità] (https://en.m.wikipedia.org/wiki/Chain-of-responsibility_pattern) –

+1

Sembra un'istruzione 'switch' sarà molto molto meglio per il codice fornito nel campione. A proposito: bella domanda. –

+1

Probabilmente ci avvicinerei estraendo i blocchi ai metodi, andando sempre più in profondità. Questo video dà un'idea di cosa intendo [Pratic Refactoring] (https://youtu.be/aWiwDdx_rdo?t=3235) (non il mio video). Mi sono collegato a un indicatore temporale pertinente, ma vale la pena guardare l'intero video. Fornisce una procedura dettagliata di refactoring del codice piuttosto orrendo ma il tuo potrebbe semplicemente batterlo. – Tone

risposta

6

È possibile utilizzare Roslyn per riscrivere il codice. Non è un buon approccio per modificare il codice sorgente come testo. Con Roslyn puoi modificarlo come un albero di sintassi.

Forse ti aiuta ad appiattire tutto?

if (a) 
if (b) F2() 
else F3(); 
else 
F4(); 

potrebbe diventare:

if (a && b) F2(); 
else if (a && !b) F3(); 
else F4(); 

In questo modo il codice sorgente diventa una semplice lista ed è più evidente in quali condizioni viene immesso un ramo.

+0

Roslyn sembra ideale per quello che spero di fare. Dato che non sono ancora a mio agio con C#, cercherò prima alcuni altri approcci, ma è molto bello sapere che esiste un tipo di strumento su cui ricorrere. (Se non altro, sarà sicuramente utile quando arriverò all'ultimo file sorgente che ho rimandato ... con un clock superiore a 25MB, di cui almeno 15MB sono caratteri tab. O_O) –

+0

Forse il tizio ha usato il Funzionalità di riorganizzatore "in linea" per incorporare molti metodi come mezzo per vendicare vendetta. Le dimensioni del codice possono aumentare in modo esponenziale quando lo fai. – usr

+0

Più guardo a questo codice, più malizioso sembra tutto. Ho modificato l'OP per dimostrare un altro livello di follia, che sembrerebbe sconfiggere l'uso di Roslyn per annullare automaticamente il danno. –