Questo articolo è un articolo speculare di traduzione automatica, clicca qui per saltare all'articolo originale.

Vista: 20516|Risposta: 0

[Consigli] Quarantasette modi per ottimizzare un programma C#

[Copiato link]
Pubblicato su 15/03/2018 10:41:59 | | |

1. Sostituire i campi accessibili con attributi

1、. Il data binding .NET supporta solo il data binding, e puoi ottenere i vantaggi del data binding usando attributi.
2. Nell'accesso get and set alla proprietà, puoi usare il lock per aggiungere il supporto multithreading.

2. readonly (costante di runtime) e const (costante di compile-time)

1. Const può essere usato solo per tipi primitivi, enum e stringhe, mentre la lettura sola può essere di qualsiasi tipo;
2. CONST sarà sostituito da una costante specifica al momento della compilazione, così che se si usano sia i valori di const che quelli di sola lettura nel riferimento, il passaggio a solo lettura cambierà l'intenzione originale del progetto, ovvero la necessità di ricompilare l'assembly modificato per rifare il nuovo valore costante.
3. La const è più efficiente della lettura sola, ma perde la flessibilità dell'applicazione.

3. IS e AS

1. Entrambi sono conversioni di tipo a runtime, poiché gli operatori possono essere usati solo nei tipi di riferimento, mentre is può usare valori e tipi di riferimento;
2. La pratica usuale è usare IS per determinare il tipo, e poi scegliere di usare come o un operatore di conversione di tipo forte (conversione definita da un operatore) in modo selettivo.

4. ConditionalAttribute invece di #if #endif条件编译

1. ConditionalAttribute viene utilizzato solo a livello di metodo, e altri elementi come tipi, attributi, ecc. sono invalidi. E #if #endif则不受此限制;
2. ConditionalAttribute può aggiungere più operazioni OR (OR) per condizioni di compilazione, e #if #endif则可以添加与(AND) [qui può essere completamente definito come un altro simbolo separato];
3. La definizione di ConditioanlAttribute può essere inserita in un metodo separato per rendere il programma più flessibile.

5. Fornire il metodo ToString()

1. Può fornire informazioni dettagliate agli utenti in modo più amichevole;
2. Usa il metodo IFormatter.ToString() per offrire una personalizzazione più flessibile, e se aggiungi le interfacce IFormatProvider e ICustomFormatter, avrà più senso personalizzare l'output del messaggio.

6. La differenza tra valore e tipo di riferimento

1. I tipi di valore non supportano il polimorfismo, che è adatto per memorizzare dati gestiti dalle applicazioni, mentre i riferimenti supportano il polimorfismo, che è adatto a definire il comportamento applicativo.
2. Per array definiti come tipi di valore, le prestazioni del programma possono essere significativamente migliorate;
3. Il tipo di valore presenta meno frammentazione della memoria dell'heap, spazzatura della memoria e tempi di accesso indiretti, e il suo ritorno nel metodo avviene sotto forma di replica per evitare di esporre la struttura interna al mondo esterno.
4. I tipi di valore sono utilizzati nei seguenti scenari: Le responsabilità dei tipi sono principalmente utilizzate per l'archiviazione dei dati; Le interfacce pubbliche sono completamente definite da alcuni attributi di accesso ai membri dei dati; Non esistono mai sottoclassi; Non esiste mai un comportamento polimorfo.

7. I tipi di valore dovrebbero essere implementati il più costante possibile e i tipi atomici

1. Rendere il nostro codice più facile da scrivere e mantenere;
2. Tre strategie per inizializzare le costanti: nella costruzione; metodo delle piante; Costruisci una classe aiutante mutabile (ad esempio StringBuilder).

8. Assicurarsi che 0 sia degno di uno status valido

1. Lo stato predefinito del tipo di valore dovrebbe essere 0;
2. Lo 0 del tipo enum non dovrebbe essere invalido; Nel FlagsAttribute serve a garantire che il valore 0 sia lo stato valido;
3. Quando la stringa è vuota, può essere restituita una stringa. Spago vuoto per vuoto.

9. Relazioni di rappresentanza multiple di giudizio uguale

1. ReferenceEquals() determina che i riferimenti sono uguali, e deve essere vero quando entrambi si riferiscono allo stesso oggetto.
2. Il metodo statico Equals() viene utilizzato per fare prima un giudizio di riferimento e poi per giudicare il tipo di valore;
3. Per il giudizio del tipo di riferimento, si può usare il metodo rewrite Equals() quando si utilizza la semantica dei valori.
4. Quando si riscrive il metodo Equals(), anche il metodo GetHashCode() deve essere riscritto, e l'operazione operater==() deve essere fornita contemporaneamente.

10. Comprendere le carenze del metodo GetHashCode()

1. GetHashCode() si applica solo ai valori hash delle chiavi definite ** basate su hash, come HashTable o Dizionario;
2. GetHashCode() deve seguire le corrispondenti tre regole: due oggetti uguali devono restituire lo stesso codice hash; dovrebbe essere un invariante di istanza; La funzione hash dovrebbe produrre una distribuzione casuale su tutti gli interi.

11. Dare priorità all'uso delle istruzioni del ciclo foreach

1. Foreach può eliminare il controllo del compilatore sul bordo dell'array del ciclo for;
2. La variabile circolare di foreach è di sola lettura, e esiste una trasformazione esplicita che genera un'eccezione quando il tipo di oggetto dell'oggetto ** è errato;
3. Il ** necessario da usare per foreach è: avere il metodo pubblico GetEnumberator(); L'interfaccia IEnumberable è esplicitamente implementata. È implementata l'interfaccia IEnumerator;
4. foreach può portare i benefici della gestione delle risorse, perché se il compilatore può determinare l'interfaccia IDisposable, può utilizzare la prova ottimizzata... infine blocca;

12. L'inizializzazione del campo predefinito è migliore dell'istruzione assignment

1. La vita del campo inizializzerà il tipo di valore a 0 e il tipo di riferimento a nullo di default.
2. Inizializzare lo stesso oggetto più volte ridurrà l'efficienza di esecuzione del codice.
3. Mettere l'inizializzazione del campo nel costruttore favorisce la gestione delle eccezioni.

13. Usare il costruttore statico per inizializzare gli elementi statici

1. Il costruttore statico verrà eseguito prima che venga accessibile qualsiasi metodo, variabile o attributo di una classe;
2. I campi statici verranno eseguiti anche prima del costruttore statico, e il costruttore statico è favorevole alla gestione delle eccezioni.

14. Usa la catena costruttore (in. NET 4.0 già risolve questo problema con parametri opzionali)

1. Usare questo per trasferire il lavoro di inizializzazione a un altro costruttore e usare base per chiamare il costruttore della classe base;
2. La sequenza operativa delle istanze di tipo è: impostare tutti i campi statici a 0; Esecuzione di inizializzatori di campo statico; un costruttore statico che esegue la classe base; Costruttori statici che eseguono il tipo corrente;
Imposta tutti i campi delle istance a 0; Esegui inizializzatori di campi di istanze; Eseguire il costruttore appropriato di istanze della classe base; Esegui il costruttore di istanza del tipo corrente.

15. Usa le istruzioni using e try/finally per ripulire le risorse

Nel metodo Dispose() dell'interfaccia IDisposable, GC.SuppressFinalize() può essere usato per notificare al garbage collector che l'operazione finale non è più stata eseguita.

16. Minimizzare la spazzatura di memoria

1. Ci vuole tempo extra allocare e distruggere gli oggetti su un heap;
2. Tecniche per ridurre il numero di oggetti assegnati: le variabili locali frequentemente usate vengono promosse a campi; Fornisce una classe che memorizza istanze comuni di oggetti Singleton che esprimono tipi specifici.
3. Utilizzare StringBuilder per eseguire operazioni di stringhe complesse.

17. Minimizzare l'imballaggio e lo scarico

1. Prestare attenzione alla conversione implicita di un tipo in System.Object, e il tipo di valore non dovrebbe essere sostituito con il tipo System.Object;
2. L'uso di interfacce invece dei tipi può evitare il boxing, cioè l'implementazione dei tipi di valore dalle interfacce e poi la chiamata dei membri tramite interfacce.

18. Implementare la modalità Dispose standard

1. Per utilizzare risorse non di memoria, deve avere un finalizer, il garbage collector aggiungerà gli oggetti finalizer implementati alla coda di terminazione dopo aver completato gli oggetti di memoria che non li hanno terminati, e poi il garbage collector avvierà un nuovo thread per eseguire i finalizer su questi oggetti. Questo può evitare il problema della perdita di memoria causata dal mancato rilascio delle risorse di memoria non gestite.
2. Utilizzando il metodo IDisposable.Dispose() richiede quattro aspetti del lavoro: rilasciare tutte le risorse non gestite; Liberare tutte le risorse gestite; Impostare un indicatore di stato per indicare se Dispose() è stato eseguito; Chiama GC.SuppressFinalize(this) per annullare l'operazione di terminazione dell'oggetto;
3. Aggiungere un metodo virtuale protetto Dispose() al tipo che necessita di polimorfismo, e la classe derivata libera il suo compito riscrivendo questo metodo.
4. Nel tipo che richiede un'interfaccia IDisoposabile, dovremmo implementare un terminator anche se non ne abbiamo bisogno.

19. Definire e implementare interfacce sui tipi di ereditarietà

1. Tipi non correlati possono implementare congiuntamente un'interfaccia comune, ed è più facile implementare un'interfaccia rispetto all'eredità;
2. L'interfaccia è relativamente stabile, racchiude un insieme di funzioni in un'interfaccia come altri tipi di implementazione si contraggono, mentre la classe base può essere estesa nel tempo.

20. Distinguere tra implementazione di interfaccia e riscrittura di metodi virtuali

1. Quando si implementa un'interfaccia nella classe base, la classe derivata deve usare new per nascondere l'uso del metodo della classe base;
2. Il metodo dell'interfaccia della classe base può essere dichiarato come un metodo virtuale e poi implementato nella classe derivata.

21. Utilizzare l'entrustment per esprimere i callback

1. Il delegato stesso non fornisce alcuna cattura eccezioni, quindi qualsiasi chiamata multicast di delegato terminerà l'intera catena di chiamata.
2. Visualizzando e chiamando ogni target di delegazione nella catena di delegati, si può evitare che i delegati multicast restituiscano solo l'output dell'ultimo delegato.

22. Utilizzare eventi per definire interfacce esterne

1. Dovrebbe essere dichiarato come un evento comune e lasciare che il compilatore crei i metodi add e renmove per noi.
2. Utilizzare il contenitore System.ComponentModel.EventHandlerList per memorizzare ogni gestore di eventi e usarlo per nascondere la complessità di tutti gli eventi quando il tipo contiene un gran numero di eventi.

23. Evitare di restituire riferimenti agli oggetti di classe interni

1. Poiché l'accesso a un oggetto di tipo valore creerà una copia dell'oggetto, gli attributi di definizione di un tipo di valore non modificheranno affatto lo stato all'interno dell'oggetto tipo;
2. I tipi costanti possono evitare di cambiare lo stato dell'oggetto;
3. Definire l'interfaccia per limitare l'accesso a un sottoinsieme al minimo del danno allo stato interno dell'oggetto.
4. Definire un oggetto wrapper per limitare l'accesso a un altro oggetto;
5. Quando il codice cliente modifica gli elementi dati interni, può essere implementata la modalità Osservatore, così che l'oggetto possa verificare o corrispondere alle modifiche.

24. La programmazione dichiarativa è migliore della programmazione imperativa

Si può evitare la possibilità di errori in più algoritmi scritti a mano simili e viene fornito un codice chiaro e leggibile.

25. Implementare i tipi il più possibile serializzabili

1. Il tipo non rappresenta un controllo, finestra o modulo UI, e il tipo dovrebbe supportare la serializzazione;
2. Quando si aggiunge l'attributo deserializzato di NonSerializedAttribute, il valore predefinito può essere caricato dal metodo OnDeserialization() che implementa IDeserializationCallback;
3. Nel controllo versioni, puoi utilizzare l'interfaccia ISerializable per un controllo flessibile e fornire un costruttore di serializzazione per inizializzare gli oggetti in base ai dati nel flusso, richiedendo anche il permesso delle eccezioni SerializationFormatter durante l'implementazione.
4. Se devi creare una classe derivata, devi fornire un metodo hook per la classe derivata.

26. Utilizzare le interfacce IComparable e IComparer per implementare le relazioni di ordinamento

1. L'interfaccia IComparable viene utilizzata per implementare la relazione di ordinamento più naturale per i tipi, sovraccaricando quattro operatori di confronto e fornendo una versione sovraccaricata del metodo CompareTo() per accettare tipi specifici come parametri.
2. IComparer viene usato per fornire relazioni di ordinamento diverse da IComparable, o per fornirci relazioni di ordinamento che il tipo stesso dice non essere implementate.

27. Evitare interfacce isolabili

1. Per i tipi di valore, non è necessario supportare l'interfaccia ICloneable, basta usare l'operazione di assegnazione predefinita;
2. Per le classi base che potrebbero dover supportare interfacce ICloneable, dovrebbe essere creato un costruttore di replicazione protetto per esse e dovrebbero essere evitate interfacce IConeable.

28. Evitare gli operatori di conversione forzata

Usare costruttori invece di operatori di conversione può rendere il lavoro di conversione più chiaro, il che può facilmente portare a bug strani dovuti agli oggetti temporanei usati dopo la conversione.

29. Considerare l'uso del nuovo modificatore solo quando l'accumulo di nuove versioni causa problemi

30. Implementare il più possibile assembly compatibili con CLS
1. Per creare un assembly compatibile, devono essere seguite due regole: i parametri e i tipi di valore di ritorno usati da tutti i membri pubblici e protetti dell'assembly devono essere compatibili con CLS; Qualsiasi membro pubblico e protetto che non sia compatibile con il CLS deve avere un'alternativa compatibile con il CLS;
2. Puoi bypassare il controllo del tipo di compatibilità CLS implementando esplicitamente l'interfaccia, e CLSCompliantAttribute non controllerà la compatibilità CLS dei membri privati.

31. Implementa un metodo breve e conciso il più possibile

1. Il compilatore JIT compila in unità di metodi, e i metodi non chiamati non saranno compilati da JIT;
2. Se il codice dell'istruzione Case nel Switch più lungo viene sostituito con un metodo alla volta, il tempo risparmiato dal compilatore JIT verrà moltiplicato;
3. Metodi brevi e concisi e la selezione di meno variabili locali possono ottenere un uso ottimizzato dei registri;
4. Meno rami di controllo nel metodo, più facile sarà per il compilatore JIT inserire variabili nei registri.

32. Realizzare assemblaggi di dimensioni piccole e ad alta coesione il più possibile

1. Mettere tutte le classi pubbliche e le classi base comuni in alcuni assembly, mettere le classi di strumenti che forniscono funzioni per le classi pubbliche nello stesso assembly, impacchettare le interfacce pubbliche rilevanti nei loro assembly e infine elaborare le classi che si trovano in tutta la posizione orizzontale dell'applicazione;
2. In linea di principio, dovrebbero essere creati due tipi di componenti: uno è un assemblaggio piccolo e aggregato con una funzione specifica, e l'altro è un insieme grande e ampio con funzioni comuni.

33. Limitare la visibilità dei tipi

1. L'uso di interfacce per esporre le funzioni dei tipi può facilitare la creazione di classi interne senza limitarne la disponibilità al di fuori dell'assembly;
2. Meno tipi pubblici sono esposti al mondo esterno, più opzioni hai per future espansioni e implementazioni di cambiamento.

34. Creare una Web API di grande granularità

Questo minimizza la frequenza e il carico delle transazioni tra macchine, mettendo al server grandi operazioni ed esecuzioni dettagliate.

35. La riscrittura è migliore dei processori di eventi

1. Se un processore di eventi lancia un'eccezione, altri processori nella catena di eventi non verranno chiamati, ma ciò non accadrà al metodo virtuale riscritto.
2. La riscrittura è molto più efficiente rispetto ai processori di eventi associativi, che devono iterare su tutta la lista delle richieste, occupando così più tempo CPU.
3. Gli eventi possono essere affrontati in tempo reale, con maggiore flessibilità, e più risposte possono essere associate allo stesso evento.
4. La regola comune è gestire un evento derivato, e il metodo di riscrittura è migliore.

36. Uso corretto. Diagnostica runtime .NET

1. System.Diagnostics.Debug\Trace\EventLog fornisce tutti gli strumenti necessari affinché il programma aggiunga informazioni diagnostiche all'esecuzione, e l'applicazione può scrivere nel registro eventi di sistema quando l'EventLog fornisce l'ingrediente;
2. Infine, non scrivere la tua libreria diagnostica, .NET FCL ha già la libreria base di cui abbiamo bisogno.

37. Utilizzare meccanismi di configurazione standard

1、. La classe System.Windows.Application del framework .NET definisce le proprietà per stabilire un percorso di configurazione comune;
2. Application.LocalAppDataPath e Application.userDataPath genereranno i nomi dei percorsi della directory dei dati locali e dei dati utente;
3. Non scrivere dati in ProgramFiles e directory di sistema Windows, queste posizioni richiedono permessi di sicurezza più elevati, non aspettarti che gli utenti abbiano permessi di scrittura.

38. Personalizzare e supportare il binding dati

1. I due oggetti di BindingManager e CurrencyManager realizzano il trasferimento dati tra il controllo e la sorgente dati;
2. Vantaggi del data binding: usare il data binding è molto più semplice che scrivere il proprio codice; Dovrebbe essere utilizzato per ambiti diversi dagli elementi di testo - anche altre proprietà di visualizzazione possono essere assegnate; Per i binding dati di Windowos Forms, la capacità di gestire la sincronizzazione multipla di controlli delle fonti dati correlate al check;
3. Quando l'oggetto non supporta gli attributi richiesti, puoi supportare il data binding bloccando l'oggetto corrente e aggiungendo l'oggetto desiderato.

39. Usa. Validazione .NET

1. Ci sono cinque controlli nel ASP.NET per verificare la validità, e puoi usare CustomValidator per derivare una nuova classe da aggiungere il tuo autenticatore.
2. La validazione di Windows richiede un sotto-System.Windows.Forms.Control.Validating per scrivere un gestore di eventi.

40. Scegli il ** appropriato in base alle esigenze

1. L'array presenta due difetti evidenti: non può essere ridimensionato dinamicamente; Il ridimensionamento richiede tempo;
2. ArrayList mescola le caratteristiche di array unidimensionali e liste collegate, Queue e Stack sono array speciali basati su Array;
3. Quando il programma è più flessibile nell'aggiungere ed eliminare elementi, può creare tipi più robusti e, creando una classe che simula **, dovrebbe implementare indicizzatori e interfacce IEnumberable per essa.

41. DataSet è migliore della struttura personalizzata

1. I DataSet presentano due svantaggi: l'interazione tra i DataSet usando il meccanismo di serializzazione XML e il codice non-.NET non è molto buona; DataSet è un contenitore molto versatile;
2. I tipi forti di DataSet infrangono più regole di progettazione, e la loro efficienza di sviluppo è molto superiore rispetto ai progetti più eleganti scritti da loro stessi.

42. Usare caratteristiche per semplificare la riflessione

Progettando e implementando classi di funzionalità che costringono gli sviluppatori a dichiarare tipi, metodi e attributi dinamicamente utilizzabili, puoi ridurre gli errori di runtime delle applicazioni e migliorare la soddisfazione degli utenti del software.

43. Evita di abusare dei riflessi

1. I parametri e i valori di ritorno usati dai membri di Invoke sono System.Object, che converte i tipi in tempo reale, ma la possibilità di problemi è diventata più probabile.
2. L'interfaccia ci permette di ottenere un sistema più chiaro e facile da mantenere, e la riflessione è un meccanismo di legame tardivo molto potente. .NET Framework lo utilizza per implementare il data binding per i controlli Windows e i controlli web.

44. Creare classi di eccezione specifiche per l'applicazione

1. L'unico motivo per cui sono necessarie classi di eccezione diverse è permettere agli utenti di adottare facilmente approcci differenti a diversi errori durante la scrittura di processori catch;
2. Quando possono esserci comportamenti di riparazione diversi, dovremmo creare una varietà di classi di eccezione diverse; fornendo tutti i costruttori supportati dalla classe base eccezioni, possiamo creare una classe eccezione completamente funzionale per l'applicazione e utilizzare l'attributo InnerException per salvare tutte le informazioni di errore generate da condizioni di errore di livello inferiore.

45. Dare priorità a garanzie di sicurezza anomale

1. La forte garanzia delle eccezioni fornisce il miglior equilibrio tra recupero dall'eccezione e gestione semplificata delle eccezioni, e lo stato del programma rimane invariato quando l'operazione viene interrotta a causa dell'eccezione.
2. Effettuare una copia difensiva dei dati da modificare, modificare la copia difensiva di questi dati, l'operazione centrale può causare un'eccezione e la copia temporanea e l'oggetto originale saranno scambiati;
3. Terminator, metodi Dispose() e metodi target vincolati ai delegati devono assicurarsi di non lanciare eccezioni in nessuna circostanza.

46. Minimizzare l'interoperabilità

1. Ci sono tre costi dell'interoperabilità: il costo dell'enumerazione dei dati tra heap gestiti e non gestiti, il costo di passaggio tra codice gestito e codice non gestito, e il lavoro di sviluppo degli sviluppatori che si occupano di ambienti ibridi;
2. L'uso del tipo blittable nell'interop può replicare efficacemente tra ambienti gestiti e non gestiti senza essere influenzato dalla struttura interna dell'oggetto.
3. Utilizzare la funzione In/Out per garantire le repliche multiple non necessarie più appropriate e migliorare le prestazioni dichiarando come i dati vengono enumerati.
4. Utilizzare l'interoperabilità COM per implementare l'interoperabilità con i componenti COM nel modo più semplice, usare P/Invoke per chiamare l'API Win32, oppure usare lo switch /CLR del compilatore C++ per mescolare codice gestito e non gestito;

47. Dare priorità ai codici di sicurezza

1. Evitare di accedere al più possibile alla memoria non gestita, e lo storage isolato non può impedire l'accesso da parte del codice gestito e degli utenti affidabili.
2. Quando gli assembly vengono eseguiti sul web, si considera l'uso di storage isolato, e quando certi algoritmi richiedono permessi di sicurezza più elevati, quei codici dovrebbero essere isolati in un assembly separato.

48. Padroneggiare strumenti e risorse rilevanti

1. Utilizzare NUnit per stabilire test unitari automatici (integrati in VS2010);
2. Lo strumento FXCop otterrà il codice IL nell'assembly, lo analizzerà rispetto alle regole di codifica eterogenee e alle migliori pratiche, e infine riporterà la violazione.
3. ILDasm è uno strumento di smontaggio IL che può aiutarci a ottenere informazioni sui dettagli;
4. Shared Source CLI è un codice sorgente di implementazione che contiene il kernel del framework .NET e il compilatore C#.




Precedente:Service Fabric - Concetto di servizio con stato
Prossimo:.net/c# SynchronizationContext per i dettagli
Disconoscimento:
Tutto il software, i materiali di programmazione o gli articoli pubblicati dalla Code Farmer Network sono destinati esclusivamente all'apprendimento e alla ricerca; I contenuti sopra elencati non devono essere utilizzati per scopi commerciali o illegali, altrimenti gli utenti dovranno sostenere tutte le conseguenze. Le informazioni su questo sito provengono da Internet, e le controversie sul copyright non hanno nulla a che fare con questo sito. Devi eliminare completamente i contenuti sopra elencati dal tuo computer entro 24 ore dal download. Se ti piace il programma, ti preghiamo di supportare software autentico, acquistare la registrazione e ottenere servizi autentici migliori. In caso di violazione, vi preghiamo di contattarci via email.

Mail To:help@itsvse.com