Questa è un'ottima domanda, in quanto ho visto un numero di sviluppatori C ++ scrivere codice C # terribile.
È meglio non pensare a C # come a "C ++ con sintassi migliore". Sono lingue diverse che richiedono approcci diversi nel tuo modo di pensare. C ++ ti obbliga a pensare costantemente a cosa faranno la CPU e la memoria. C # non è così C # è progettato in modo specifico per non pensare alla CPU e alla memoria e invece pensa al dominio aziendale che stai scrivendo .
Un esempio di ciò che ho visto è che molti sviluppatori C ++ vorrebbero usare i loop perché sono più veloci di foreach. Di solito questa è una pessima idea in C # perché limita i possibili tipi di raccolta su cui è stata eseguita l'iterazione (e quindi la riutilizzabilità e la flessibilità del codice).
Penso che il modo migliore per riadattarsi da C ++ a C # sia cercare di avvicinarsi alla codifica da una prospettiva diversa. All'inizio questo sarà difficile, perché nel corso degli anni sarai completamente abituato a usare il thread "che cosa fa 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" al contrario di "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 prende un IEnumerable<MyObject>
e usa un ciclo foreach
.
I tuoi esempi specifici:
Controlling the lifetime of resources that require deterministic cleanup (like files). This is easy having using in hand but how to use it properly when ownership of the resource is being transfered [... betwen threads]? In C++ I would simply use shared pointers and let it take care of 'garbage collection' just at the right time.
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 tue classi, ma non cercare di passare un file tra i thread e avere classi separate di scrittura / lettura / chiusura / apertura. Non condividere la proprietà delle risorse non gestite. Utilizza il modello Dispose
per gestire la pulizia.
Constant struggling with overriding functions for specific generics (I love things like partial template specialization in C++). Should I just abandon any attempts to do any generic programming in C#? Maybe generics are limited on purpose and it is not C#-ish to use them except for a specific domain of problems?
I generici in C # sono progettati per essere generici. Le specializzazioni di generici dovrebbero essere gestite da classi derivate. Perché un List<T>
dovrebbe comportarsi diversamente se è un List<int>
o un List<string>
? Tutte le operazioni su List<T>
sono generiche in modo da applicare a qualsiasi List<T>
. Se si desidera modificare il comportamento del metodo .Add
su List<string>
, quindi creare una classe derivata MySpecializedStringCollection : List<string>
o una classe composta MySpecializedStringCollection : IList<string>
che utilizza internamente il generico ma fa le cose in modo diverso. Questo ti aiuta a evitare di violare il Principio di sostituzione di Liskov e di rovinare a fondo gli altri che usano la tua classe.
Macro-like functionality. While generally a bad idea, for some domain of problems there is no other workaround (e.g. conditional evaluation of a statement, like with logs that should only go to Debug releases). Not having them means that I need to put more if (condition) {...} boilerplate and it is still not equal in terms of triggering side effects.
Come altri hanno già detto, puoi usare i comandi del preprocessore per farlo. Gli attributi sono ancora meglio. 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. Puoi ottimizzare in seguito in seguito, ma il tuo codice dovrebbe riflettere le relazioni tra i principi aziendali che stai tentando di mappare.