Questa è un'ottima domanda, dato che ho visto diversi sviluppatori C ++ scrivere codice C # terribile.
È meglio non pensare a C # come "C ++ con una sintassi migliore". Sono lingue diverse che richiedono approcci diversi nel tuo modo di pensare. Il C ++ ti obbliga a pensare costantemente a cosa faranno la CPU e la memoria. C # non è così. C # è progettato specificamente per non pensare alla CPU e alla memoria e invece al dominio aziendale per cui stai scrivendo .
Un esempio di ciò che ho visto è che molti sviluppatori C ++ adoreranno usare i loop perché sono più veloci di foreach. Di solito questa è una cattiva idea in C # perché limita i possibili tipi della raccolta su cui si esegue l'iterazione (e quindi la riusabilità e la flessibilità del codice).
Penso che il modo migliore per riadattare da C ++ a C # sia cercare di approcciare la programmazione da una prospettiva diversa. All'inizio questo sarà difficile, perché nel corso degli anni sarai completamente abituato a usare il thread "cosa stanno facendo la CPU e la memoria" nel tuo cervello per filtrare il codice che scrivi. Ma in C #, dovresti invece pensare alle relazioni tra gli oggetti nel dominio aziendale. "Cosa voglio fare" anziché "cosa sta facendo il computer".
Se vuoi fare qualcosa per tutto in un elenco di oggetti, invece di scrivere un ciclo for nell'elenco, crea un metodo che accetta un IEnumerable<MyObject>
e usa un foreach
ciclo.
I tuoi esempi specifici:
Controllo della durata delle risorse che richiedono una pulizia deterministica (come i file). È facile averlo a portata di mano, ma come usarlo correttamente quando la proprietà della risorsa viene trasferita [... tra i thread]? In C ++ userei semplicemente i puntatori condivisi e lascerei che si occupasse della "garbage collection" al momento giusto.
Non dovresti mai farlo in C # (in particolare tra i thread). Se devi fare qualcosa su un file, fallo in un posto alla volta. Va bene (e in effetti è una buona pratica) scrivere una classe wrapper che gestisca la risorsa non gestita che viene passata tra le classi, ma non provare a passare un file tra i thread e avere classi separate scrivere / leggere / chiudere / aprirlo. Non condividere la proprietà di risorse non gestite. Usa il Dispose
modello per gestire la pulizia.
Difficoltà costante con funzioni prioritarie per generici specifici (amo le cose come la specializzazione parziale dei template in C ++). Dovrei semplicemente abbandonare qualsiasi tentativo di fare una programmazione generica in C #? Forse i generici sono limitati di proposito e non è C # -ish usarli se non per uno specifico dominio di problemi?
I generici in C # sono progettati per essere generici. Le specializzazioni dei generici dovrebbero essere gestite da classi derivate. Perché un List<T>
comportamento dovrebbe essere diverso se si tratta di a List<int>
o a List<string>
? Tutte le operazioni su List<T>
sono generiche in modo da applicarsi a qualsiasi List<T>
. Se si desidera modificare il comportamento del .Add
metodo su a List<string>
, quindi creare una classe derivata MySpecializedStringCollection : List<string>
o una classe composta MySpecializedStringCollection : IList<string>
che utilizza il generico internamente ma fa le cose in modo diverso. Questo ti aiuta a evitare di violare il principio di sostituzione di Liskov e di fregare in modo regale gli altri che usano la tua classe.
Funzionalità macro-simile. Sebbene generalmente sia una cattiva idea, per alcuni domini di problemi non c'è altra soluzione alternativa (ad esempio la valutazione condizionale di un'istruzione, come con i log che dovrebbero andare solo alle versioni di debug). Non averli significa che devo aggiungere altro se (condition) {...} boilerplate e non è ancora uguale in termini di innesco di effetti collaterali.
Come altri hanno già detto, è possibile utilizzare i comandi del preprocessore per farlo. Gli attributi sono ancora migliori. In generale, gli attributi sono il modo migliore per gestire cose che non sono funzionalità "core" per una classe.
In breve, quando scrivi C #, tieni presente che dovresti pensare interamente al dominio aziendale e non alla CPU e alla memoria. È possibile ottimizzare in un secondo momento, se necessario , ma il codice dovrebbe riflettere le relazioni tra i principi aziendali che si sta tentando di mappare.
C#
per generare altro codice. Puoi leggere unCSV
o unXML
o cosa hai come file di input e generare unC#
o unSQL
file. Questo può essere più potente dell'utilizzo di macro funzionali.