1. Sustituir los campos accesibles por atributos
1、. La vinculación de datos .NET solo soporta vinculación de datos, y puedes obtener beneficios de la vinculación de datos usando atributos. 2. En el acceso get and set a la propiedad, puedes usar el lock para añadir soporte multihilo.
2. solo lectura (constante de ejecución) y const (constante de compilación)
1. const solo puede usarse para tipos primitivos, enums y cadenas, mientras que la de solo lectura puede ser de cualquier tipo; 2. Const será reemplazado por una constante específica en tiempo de compilación, de modo que si se usan tanto valores de const como de solo lectura en la referencia, el cambio a solo lectura cambiará la intención original del diseño, que es la necesidad de recompilar el ensamblador modificado para volver a referenciar el nuevo valor constante. 3. La const es más eficiente que la de solo lectura, pero pierde la flexibilidad de aplicación.
3. IS y AS
1. Ambos son conversiones de tipos en tiempo de ejecución, ya que los operadores solo pueden usarse en tipos de referencia, mientras que puede usar valores y tipos de referencia; 2. La práctica habitual es usar IS para determinar el tipo, y luego elegir usar como o un operador de conversión de tipo fuerte (conversión definida por un operador) selectivamente.
4. ConditionalAttribute en lugar de #if #endif条件编译
1. ConditionalAttribute solo se usa a nivel de método, y otros elementos como tipos, atributos, etc., son inválidos. Y #if #endif则不受此限制; 2. ConditionalAttribute puede añadir múltiples operaciones OR (OR) para condiciones de compilación, y #if #endif则可以添加与(AND) [aquí puede definirse completamente como otro símbolo separado]; 3. La definición de ConditioanlAttribute puede colocarse en un método separado para hacer el programa más flexible.
5. Proporcionar el método ToString()
1. Puede proporcionar información detallada a los usuarios de forma más amigable; 2. Utiliza el método IFormatter.ToString() para ofrecer una personalización más flexible, y si añades las interfaces IFormatProvider e ICustomFormatter, tendrá más sentido personalizar la salida del mensaje.
6. La diferencia entre valor y tipo de referencia
1. Los tipos de valor no soportan polimorfismo, que es adecuado para almacenar datos operados por aplicaciones, mientras que las referencias admiten polimorfismo, que es adecuado para definir el comportamiento de las aplicaciones. 2. Para arrays definidos como tipos de valores, el rendimiento del programa puede mejorarse significativamente; 3. El tipo de valor tiene menos fragmentación de memoria heap, basura de memoria y tiempo de acceso indirecto, y su retorno en el método se realiza en forma de replicación para evitar exponer la estructura interna al mundo exterior. 4. Los tipos de valor se utilizan en los siguientes escenarios: Las responsabilidades de los tipos se emplean principalmente para el almacenamiento de datos; Las interfaces públicas están completamente definidas por algunos atributos de acceso de los miembros de datos; Nunca existen subclases; Nunca existe un comportamiento polimórfico.
7. Los tipos de valor deben implementarse tan constantes y tipos atómicos como sea posible
1. Facilitar la escritura y el mantenimiento de nuestro código; 2. Tres estrategias para inicializar constantes: en construcción; método vegetal; Construye una clase ayudante mutable (por ejemplo, StringBuilder).
8. Asegurarse de que el 0 sea digno de estatus válido
1. El estado predeterminado del tipo de valor debe ser 0; 2. El 0 del tipo de enum no debe ser inválido; En el FlagsAttribute se debe asegurar que el valor 0 sea un estado válido; 3. Cuando la cadena está vacía, se puede devolver una cadena. Cuerda vacía para vacía.
9. Múltiples relaciones de representación de juicio igual
1. ReferenceEquals() determina que las referencias son iguales, y debe ser cierto cuando ambas se refieren al mismo objeto. 2. El método estático Equals() se utiliza primero para hacer juicio de referencia y luego para juzgar el tipo de valor; 3. Para el juicio del tipo de referencia, puedes usar el método de reescritura Igual() al usar semántica de valores. 4. Al reescribir el método Equals(), también debe reescribirse el método GetHashCode(), y la operación operater==() debe proporcionarse al mismo tiempo.
10. Comprender las limitaciones del método GetHashCode()
1. GetHashCode() solo se aplica a valores hash de claves definidas por hash, como HashTable o Dictionary; 2. GetHashCode() debe seguir las tres reglas correspondientes: dos objetos iguales deben devolver el mismo código hash; debe ser un invariante de instancia; La función hash debe producir una distribución aleatoria entre todos los enteros.
11. Dar prioridad al uso de sentencias de bucle foreach
1. Foreach puede eliminar la comprobación del límite del array del bucle for por parte del compilador; 2. La variable circular de foreach es de solo lectura, y existe una transformación explícita que genera una excepción cuando el tipo de objeto del objeto ** es incorrecto; 3. El ** necesario para foreach es: tener el método público GetEnumberator(); La interfaz IEnumberable está implementada explícitamente. Se implementa la interfaz de IEnumerator; 4. foreach puede aportar los beneficios de la gestión de recursos, porque si el compilador puede determinar la interfaz IDisposable, puede usar el optimizado try... finalmente bloquear;
12. La inicialización del campo por defecto es mejor que la instrucción de asignación
1. La vida útil del campo inicializará el tipo de valor a 0 y el tipo de referencia a nulo por defecto. 2. Inicializar el mismo objeto varias veces reducirá la eficiencia de ejecución del código. 3. Poner la inicialización del campo en el constructor favorece el manejo de excepciones.
13. Utilizar el constructor estático para inicializar los miembros estáticos
1. El constructor estático se ejecutará antes de acceder a cualquier método, variable o atributo de una clase; 2. Los campos estáticos también se ejecutarán antes que el constructor estático, y el constructor estático es propicio para el manejo de excepciones.
14. Usar la cadena constructora (en NET 4.0 ya soluciona este problema con parámetros opcionales)
1. Usar esto para transferir el trabajo de inicialización a otro constructor, y usar base para llamar al constructor de la clase base; 2. La secuencia de operaciones de instancias de tipo es: establecer todos los campos estáticos a 0; Ejecución de inicializadores de campos estáticos; un constructor estático que ejecuta la clase base; Constructores estáticos que ejecutan el tipo actual; Establece todos los campos de instancia en 0; Ejecutar inicializadores de campos de instancia; Ejecutar el constructor de instancias de clase base correspondiente; Ejecuta el constructor de instancias del tipo actual.
15. Usar las sentencias using y try/finally para limpiar recursos
En el método Dispose() de la interfaz IDisposable, GC.SuppressFinalize() puede usarse para notificar al recogedor de basura que la operación final ya no se ha realizado.
16. Minimizar la basura de memoria
1. Se necesita tiempo extra al procesador para asignar y destruir objetos en un heap; 2. Técnicas para reducir el número de objetos asignados: las variables locales frecuentemente usadas se promueven a campos; Proporciona una clase que almacena instancias comunes de objetos Singleton que expresan tipos específicos. 3. Utilizar StringBuilder para realizar operaciones complejas de cadenas.
17. Minimizar el empaquetado y el desembalaje
1. Prestar atención a la conversión implícita de un tipo a System.Object, y el tipo de valor no debe reemplazarse por System.Object type; 2. Utilizar interfaces en lugar de tipos puede evitar el boxeo, es decir, implementar tipos de valor desde interfaces y luego llamar a los miembros a través de interfaces.
18. Implementar el modo Disposal estándar
1. Para usar recursos que no sean de memoria, debe tener un finalizador, el recolector de basura añadirá los objetos finalizadores implementados a la cola de terminación tras completar los objetos de memoria que no los han terminado, y luego el recolector de basura iniciará un nuevo hilo para ejecutar los finalizadores en estos objetos. Esto puede evitar el problema de fuga de memoria causada por la falta de liberación de recursos de memoria no gestionados. 2. Utilizar el método IDisposable.Dispose() requiere cuatro aspectos del trabajo: liberar todos los recursos no gestionados; liberar todos los recursos gestionados; Establecer un marcador de estado para indicar si Dispose() ha sido ejecutado; Llama a GC.SuppressFinalize(this) para cancelar la operación de terminación del objeto; 3. Añadir un método virtual protegido Dispose() al tipo que necesita polimorfismo, y la clase derivada libera su tarea reescribiendo este método. 4. En el tipo que requiere una interfaz IDisoposable, deberíamos implementar un terminator aunque no lo necesitemos.
19. Definir e implementar interfaces sobre tipos de herencia
1. Tipos no relacionados pueden implementar conjuntamente una interfaz común, y es más fácil implementar una interfaz que la herencia; 2. La interfaz es relativamente estable, encapsula un conjunto de funciones en una interfaz como otros tipos de contratos de implementación, mientras que la clase base puede extenderse con el tiempo.
20. Distinguir entre la implementación de interfaces y la reescritura de métodos virtuales
1. Al implementar una interfaz en la clase base, la clase derivada necesita usar new para ocultar el uso del método de clase base; 2. El método de la interfaz de clase base puede declararse como un método virtual y luego implementarse en la clase derivada.
21. Utilizar la confianza para expresar devolución de llamada
1. El delegado en sí no proporciona captura de excepciones, por lo que cualquier llamada multicast a delegado terminará toda la cadena de llamadas. 2. Mostrando y llamando a cada destino de delegación en la cadena de delegados, se puede evitar que los delegados multicast devuelvan solo la salida del último delegado.
22. Utilizar eventos para definir interfaces externas
1. Debe declararse como un evento común y dejar que el compilador cree métodos de adición y remoción para nosotros. 2. Utilizar el contenedor System.ComponentModel.EventHandlerList para almacenar cada gestor de eventos, y emplearlo para ocultar la complejidad de todos los eventos cuando el tipo contiene un gran número de eventos.
23. Evitar devolver referencias a objetos de clase internos
1. Dado que el acceso a un objeto tipo valor creará una copia del objeto, los atributos de definir un tipo de valor no cambiarán el estado dentro del objeto tipo en absoluto; 2. Los tipos constantes pueden evitar cambiar el estado del objeto; 3. Definir la interfaz para limitar el acceso a un subconjunto y minimizar el daño al estado interno del objeto. 4. Definir un objeto envoltorio para restringir el acceso a otro objeto; 5. Cuando el código cliente cambia los elementos de datos internos, se puede implementar el modo Observador, de modo que el objeto pueda verificar o corresponder a los cambios.
24. La programación declarativa es mejor que la programación imperativa
Se puede evitar la posibilidad de cometer errores en varios algoritmos manuscritos similares y se proporciona código claro y legible.
25. Tipos de implementación lo más serializables posible
1. El tipo no representa un control de interfaz, ventana o formulario, y el tipo debe soportar serialización; 2. Al añadir el atributo deserializado de NonSerializedAttribute, el valor predeterminado puede cargarse mediante el método OnDeserialization() que implementa IDeserializationCallback; 3. En control de versiones, puedes usar la interfaz ISerializable para un control flexible, y proporcionar un constructor de serialización para inicializar objetos según los datos del flujo, y también requerir el permiso de las excepciones de SerializationFormatter al implementarlo. 4. Si necesitas crear una clase derivada, necesitas proporcionar un método de gancho para la clase derivada.
26. Utilizar interfaces IComparable e IComparer para implementar relaciones de ordenación
1. La interfaz IComparable se utiliza para implementar la relación de ordenación más natural para tipos, sobrecargando cuatro operadores de comparación y proporcionando una versión sobrecargada del método CompareTo() para aceptar tipos específicos como parámetros. 2. IComparer se utiliza para proporcionar relaciones de ordenación que son diferentes de IComparable, o para proporcionarnos relaciones de ordenación que el propio tipo indica que no están implementadas.
27. Evitar interfaces isoleables
1. Para los tipos de valor, no es necesario soportar la interfaz ICloneable, solo usar la operación de asignación predeterminada; 2. Para las clases base que puedan necesitar soportar interfaces ICloneables, debe crearse un constructor de replicación protegido para ellas y evitar interfaces IConeables.
28. Evitar operadores de conversión forzada
Usar constructores en lugar de operadores de conversión puede hacer que el trabajo de conversión sea más claro, lo que puede llevar fácilmente a errores extraños debido a objetos temporales usados tras la conversión.
29. Solo considera usar el nuevo modificador cuando la acumulación de nuevas versiones cause problemas
30. Implementar ensamblajes compatibles con CLS tanto como sea posible 1. Para crear un ensamblador compatible, se deben seguir dos reglas: los parámetros y tipos de valor de retorno usados por todos los miembros públicos y protegidos del ensamblador deben ser compatibles con CLS; Cualquier miembro público y protegido que no sea compatible con el CLS debe tener una alternativa compatible con el CLS; 2. Puedes saltarte la comprobación de tipo de compatibilidad CLS implementando explícitamente la interfaz, y el CLSCompliantAttribute no comprobará la compatibilidad CLS de los miembros privados.
31. Implementar un método breve y conciso tanto como sea posible
1. El compilador JIT compila en unidades de métodos, y los métodos que no se llaman no serán compilados por JIT; 2. Si el código de la instrucción Case en el Switch más largo se reemplaza por un método a la vez, el tiempo ahorrado por el compilador JIT se multiplicará; 3. Métodos cortos y concisos y la selección de menos variables locales pueden lograr un uso optimizado de los registros; 4. Cuantas menos ramas de control tenga el método, más fácil será para el compilador JIT colocar variables en los registros.
32. Realizar ensamblajes de tamaño pequeño y alta cohesión tanto como sea posible
1. Poner todas las clases públicas y las clases base comunes en algunos ensamblajes, poner las clases de herramientas que proporcionan funciones para las clases públicas en el mismo ensamblador, empaquetar las interfaces públicas relevantes en sus propios ensamblajes y, finalmente, procesar las clases que están en posición horizontal en la aplicación; 2. En principio, deben crearse dos tipos de componentes: uno es un conjunto pequeño y agregado con una función específica, y el otro es un conjunto grande y ancho con funciones comunes.
33. Limitar la visibilidad de los tipos
1. Utilizar interfaces para exponer las funciones de los tipos puede facilitar la creación de clases internas sin limitar su disponibilidad fuera del ensamblaje; 2. Cuantos menos tipos públicos estén expuestos al mundo exterior, más opciones tendrás para futuras expansiones e implementaciones de cambio.
34. Crear una API web de gran tamaño granular
Esto minimiza la frecuencia y carga de las transacciones entre máquinas, trasladando grandes operaciones y ejecuciones detalladas al servidor.
35. Reescribir es mejor que procesadores de eventos
1. Si un procesador de eventos lanza una excepción, no se llamarán otros procesadores en la cadena de eventos, pero esto no ocurrirá con el método virtual reescrito. 2. La reescritura es mucho más eficiente que los procesadores de eventos asociativos, que necesitan iterar sobre toda la lista de solicitudes, lo que ocupa más tiempo de CPU. 3. Los eventos pueden responderse en tiempo de ejecución, con mayor flexibilidad, y múltiples respuestas pueden asociarse al mismo evento. 4. La regla común es tratar con un evento derivado, y el método de reescritura es mejor.
36. Uso legítimo. Diagnóstico en tiempo de ejecución de .NET
1. System.Diagnostics.Debug\Trace\EventLog proporciona todas las herramientas necesarias para que el programa añada información de diagnóstico al tiempo de ejecución, y la aplicación puede escribir en el registro de eventos del sistema cuando el EventLog proporciona el ingrediente; 2. Por último, no escribas tu propia biblioteca de diagnóstico, .NET FCL ya tiene la biblioteca central que necesitamos.
37. Utilizar mecanismos de configuración estándar
1、. La clase System.Windows.Application del framework .NET define las propiedades para establecer un camino de configuración común; 2. Application.LocalAppDataPath y Application.userDataPath generarán los nombres de rutas del directorio de datos locales y los datos de usuario; 3. No escribas datos en ProgramFiles ni en directorios del sistema Windows, estas ubicaciones requieren permisos de seguridad más altos, no esperes que los usuarios tengan permisos de escritura.
38. Personalizar y soportar la vinculación de datos
1. Los dos objetos de BindingManager y CurrencyManager realizan la transferencia de datos entre el control y la fuente de datos; 2. Ventajas de la vinculación de datos: usar enlace de datos es mucho más sencillo que escribir tu propio código; Debe usarse para ámbitos distintos a los elementos de datos de texto; otras propiedades de visualización también pueden ser acotadas; Para las vinculaciones de datos de WindowOS Forms, la capacidad de gestionar la sincronización de múltiples controles de fuentes de datos relacionadas con comprobaciones; 3. Cuando el objeto no soporta los atributos requeridos, puedes soportar la vinculación de datos bloqueando el objeto actual y luego añadiendo el objeto deseado.
39. Usar. Validación .NET
1. Hay cinco controles en la ASP.NET para verificar la validez, y puedes usar CustomValidator para derivar una nueva clase que añada tu propio autenticador. 2. La validación de Windows requiere un subsistema.Windows.Forms.Control.Validating para escribir un gestor de eventos.
40. Elige el ** adecuado según las necesidades
1. El array tiene dos defectos evidentes: no puede redimensionarse dinámicamente; Redimensionar es laborioso; 2. ArrayList mezcla las características de arrays unidimensionales y listas enlazadas, Queue y Stack son arrays especiales basados en Array; 3. Cuando el programa es más flexible para añadir y eliminar elementos, puede crear tipos más robustos, y al crear una clase que simule **, debe implementar indexadores e interfaces IEnumerables para ella.
41. DataSet es mejor que la estructura personalizada
1. Los conjuntos de datos tienen dos desventajas: la interacción entre los conjuntos de datos usando el mecanismo de serialización XML y el código non-.NET no es muy buena; DataSet es un contenedor muy versátil; 2. Los tipos fuertes de DataSets rompen más reglas de diseño, y su eficiencia de desarrollo es mucho mayor que la de los diseños más elegantes escritos por ellos mismos.
42. Utilizar características para simplificar la reflexión
Al diseñar e implementar clases de características que obliguen a los desarrolladores a declarar tipos, métodos y atributos dinámicamente utilizables, puedes reducir los errores en tiempo de ejecución de la aplicación y mejorar la satisfacción del usuario de software.
43. Evita abusar de los reflejos
1. Los parámetros y valores de retorno usados por los miembros de Invoke son System.Object, que convierte tipos en tiempo de ejecución, pero la posibilidad de problemas se ha vuelto más probable. 2. La interfaz nos permite obtener un sistema más claro y mantenible, y la reflexión es un mecanismo de enlace tardío muy potente. .NET Framework lo utiliza para implementar la vinculación de datos para controles de Windows y web.
44. Crear clases de excepción específicas para la aplicación
1. La única razón por la que se necesitan diferentes clases de excepción es para permitir que los usuarios adopten fácilmente diferentes enfoques para distintos errores al escribir procesadores catch; 2. Cuando pueda haber diferentes comportamientos de reparación, deberíamos crear una variedad de clases de excepción distintas; proporcionando todos los constructores soportados por la clase base de excepción, podemos crear una clase de excepción completamente funcional para la aplicación y usar el atributo InnerException para guardar toda la información de error generada por condiciones de error de menor nivel.
45. Dar prioridad a garantías de seguridad anormales
1. La garantía de excepciones fuertes proporciona el mejor equilibrio entre la recuperación de la excepción y la gestión simplificada de excepciones, y el estado del programa permanece sin cambios cuando la operación se interrumpe debido a la excepción. 2. Hacer copia defensiva de los datos a modificar, modificar la copia defensiva de estos datos, la operación en el centro puede causar una excepción, y la copia temporal y el objeto original se intercambiarán; 3. Los métodos Terminators, Dispose() y los métodos objetivo vinculados a los delegados deben asegurarse de que no generen excepciones bajo ninguna circunstancia.
46. Minimizar la interoperabilidad
1. Existen tres costes de interoperabilidad: el coste de enumeración de datos entre heaps gestionados y no gestionados, el coste de cambiar entre código gestionado y código no gestionado, y el trabajo de desarrollo de desarrolladores que trabajan con entornos híbridos; 2. El uso del tipo blittable en interop puede replicar eficazmente entre entornos gestionados y no gestionados sin verse afectado por la estructura interna del objeto. 3. Utilizar la función In/Out para asegurar las replicaciones múltiples innecesarias más adecuadas y mejorar el rendimiento declarando cómo se enumeran los datos. 4. Utilizar interoperabilidad COM para implementar interoperabilidad con componentes COM de la forma más sencilla, usar P/Invoke para llamar a la API Win32, o usar el switch /CLR del compilador C++ para mezclar código gestionado y no gestionado;
47. Dar prioridad a los códigos de seguridad
1. Evitar acceder a la memoria no gestionada tanto como sea posible, y el almacenamiento aislado no puede impedir el acceso de código gestionado y usuarios de confianza. 2. Cuando los ensambladores se ejecutan en la web, considera usar almacenamiento aislado, y cuando ciertos algoritmos requieren permisos de seguridad más altos, esos códigos deben aislarse en un ensamblador separado.
48. Domina las herramientas y recursos relevantes
1. Utilizar NUnit para establecer pruebas unitarias automáticas (integradas en VS2010); 2. La herramienta FXCop obtendrá el código IL en el ensamblaje, lo analizará frente a las reglas de codificación heterogéneas y mejores prácticas, y finalmente reportará la infracción. 3. ILDasm es una herramienta de desmontaje de IL que puede ayudarnos a obtener información sobre los detalles; 4. La CLI de código fuente compartido es un código fuente de implementación que contiene el núcleo del .NET framework y el compilador de C#. |