1. Prepare for C# 2.0 (This rule is meaningless now, after all, it has reached 4.0 now) Understand the ECM standard
2. Implement the type as serializable as possible. 1. The type does not represent UI controls, windows or forms. The type should support serialization; 2. When adding the deserialized attribute of NonSerializedAttribute, the default value can be loaded by the OnDeserialization() method of IDeserializationCallback; 3. In version control, the ISerializable interface can be used for flexible control, and a serialized constructor can be provided to initialize the object according to the data in the stream. During implementation, the permission of the SerializationFormatter exception is also required; 4. If you need to create a derived class, you need to provide a hook method for the derived class to use.
3. Priority is used foreach loop statement 1. Foreach can eliminate the compiler's check on the array boundary of the for loop; 2. Foreach's loop variables are read-only and there is an explicit conversion, throwing exceptions when the object type of the ** object is incorrect; 3. The ** used by foreach include: having the public GetEnumberator() method; explicitly implementing the IEnumberable interface; implementing the IEnumerator interface; 4. Foreach can bring the benefits of resource management, because if the compiler can determine the IDisposable interface, the optimized try... finally block can be used;
4. readonly (runtime constant) and const (compilation constant) 1. const can only be used for primitive types, enums, and strings, while readonly can be any type; 2. const will be replaced with specific constants at compile time. In this way, if two values of const and readonly are used in the reference, the change of readonly will change the original intention of the design. This requires recompiling the changed assembly to re-reference the new constant value. 3. Const is more efficient than readonly, but loses the flexibility of application.
5. The initialization of the default field is better than the assignment statement. 1. The field life will initialize the value type to 0 by default and the reference type will be null; 2. Initializing the same object multiple times will reduce the execution efficiency of the code; 3. Putting the initialization of the field into the constructor is conducive to exception processing.
6. Avoid excessive use of reflection 1. The parameters and return values used by Invoke members are System.Object, and type conversion is performed at runtime, but the possibility of problems has become more likely; 2. The interface allows us to obtain a clearer and more maintainable system. Reflection is a very powerful late-stage binding mechanism. The .NET framework uses it to implement data binding of Windows controls and web controls.
7. Minimize memory garbage 1. It takes extra processor time to allocate and destroy objects on a heap; 2. Tips to reduce the number of allocated objects: often used local variables to be promoted to fields; provide a class to store Singleton objects to express common instances of specific types. 3. Use StringBuilder to perform complex string operations.
8. Use the constructor chain (the problem has been solved with optional parameters in .NET 4.0) 1. Use this to hand over the initialization work to another constructor, and use base to call the base class constructor; 2. The operation order of type instances is: set all static fields to 0; execute static field initializer; execute static constructor of base class; execute static constructor of current type; set all instance fields to 0; execute instance field initializer; execute appropriate base class instance constructor; execute current type instance constructor.
9. Use .NET verification 1. There are five controls in ASP.NET to verify validity. You can use CustomValidator to derive a new class to add your own authenticator; 2. Windows verification requires a subSystem.Windows.Forms.Control.Validating to write an event processor.
10. Minimize boxing and unboxing as much as possible 1. Pay attention to the implicit conversion of a type to System.Object. At the same time, the value type should not be replaced with the System.Object type; 2. Using an interface instead of a type can avoid boxing, that is, implementing the value type from the interface and then calling members through the interface.
11. The difference between value and reference types 1. The value type does not support polymorphism, which is suitable for storing data operated by the application, while references support polymorphism, which is suitable for defining the behavior of the application; 2. Defining arrays as value types can significantly improve the performance of the program; 3. The value type has less heap memory fragmentation, memory garbage and indirect access time, and its return in the method is performed in a copy way to avoid exposing the internal structure to the outside world; 4. The value type is applied in the following scenarios: the responsibility of the type is mainly used for data storage; the public interface is completely defined by some data members' access attributes; there is never a subclass; there is never a polymorphic behavior.
12. Value types are implemented as constant and atomic as possible. 1. Make our code easier to write and maintain; 2. Three strategies for initializing constants: in construction; factory methods; constructing a variable auxiliary class (such as StringBuilder).
13. Choose the appropriate ** according to needs. 1. Arrays have two obvious shortcomings: they cannot dynamically resize; they are very time-consuming to adjust; 2. ArrayList mixes the characteristics of one-dimensional arrays and linked lists. Queue and Stack are special arrays based on Array; 3. When the program adds and deletes items more flexibly, it can make a more robust ** type. When creating a class that simulates **, it should implement the indexer and IEnumberable interface for it.
14. Create specific exception classes for the application 1. The only reason for requiring different exception classes is to allow users to easily adopt different approaches to different errors when writing catch processors; 2. When there may be different repair behaviors, we should create multiple different exception classes. By providing all constructors supported by the exception base class, we can create complete functions for the application. Using the InnerException property can save all error information generated by lower-level error conditions.
15. Ensure that 0 is a valid state worth 1. The default state of the value type should be 0; 2. Enumeration type 0 should not be an invalid state; when FlagsAttribute is a valid state, it should ensure that the value 0 is a valid state; 3. When the string is empty, an empty string of string.Empty can be returned.
16. ConditionalAttribute is used instead of #if#endif conditional compilation 1. ConditionalAttribute is only used at the method level, and is invalid for other additions such as types, attributes, etc.; while #if#endif is not subject to this limitation; 2. ConditionalAttribute can add multiple compilation conditions or (OR) operations, while #if#endif can add and (AND) [here can be completely defined as another separate symbol]; 3. ConditionalAttribute definition can be placed in a separate method, making the program more flexible.
17. Declarative programming is superior to imperative programming to avoid the possibility of making mistakes in multiple similar handwritten algorithms and provide clear and readable code.
18. Minimize interoperability 1. Interoperability has three costs: the enumeration cost of data between the managed heap and the unmanaged heap, the cost of switching between managed code and unmanaged code, and the development work for developers to deal with hybrid environments; 2. Using blittable type in interop can effectively replicate in managed and unmanaged environments without being affected by the internal structure of the object; 3. Using the In/Out feature to ensure the most appropriate and unnecessary multiple replications, and improve performance by declaring how the data is enumerated; 4. Using COMInterop to achieve interoperability with COM components in the simplest way, using P/Invoke to call Win32API, or using the C++ compiler's /CLR switch to mix managed and unmanaged code;
19. Consider using the new modifier only when the accumulation of new versions causes problems.
20. Priority for security code 1. Avoid access to unmanaged memory as much as possible. Isolated storage cannot prevent access from managed code and trusted users; 2. Isolated storage can be considered when assemblies run on the web. When some algorithms do require higher security permissions, those codes should be isolated in a separate assembly.
21. Implement standard Dispose mode 1. Use non-memory resources. It must have a finalizer. After the garbage collector completes the memory object that has not terminated, it will add the implemented finalizer object to the final queue. Then the garbage collector will start a new thread to run the finalizer on these objects. This defensive way is because if the user forgets to call the Dispose() method, the garbage collector will always call the finalizer method, which can avoid the problem of unmanaged memory resources not being released and causing memory leakage; 2. Use IDisposable.Di The spose() method requires four aspects of work: release all unmanaged resources; release all managed resources; set a status tag to indicate whether Dispose() has been executed; call GC.SuppressFinalize (this) to cancel the object's termination operation; 3. Add a protected virtual method Dispose() for the type that requires polymorphism, and the derived class releases its own tasks by rewriting this method; 4. In types that require IDisoposable interface, a terminator should be implemented even if we do not need a terminator.
22. Create a large-grained WebAPI. This will minimize the frequency and load of transactions between machines, and place large operations and fine-grained execution on the server to execute.
23. Implement short and concise methods as much as possible. 1. The JIT compiler compiles in units of methods. Methods that have not been called will not be compiled by JIT; 2. If the code of the Case statement in a longer Switch is replaced with methods one by one, the time saved by the JIT compiler will be multiplied; 3. A short and concise method and selecting fewer local variables can obtain optimized register use; 4. The fewer control branches in the method, the easier it is for the JIT compiler to put variables into registers.
24. Preferential exception security guarantee 1. Strong exception guarantee provides the best balance between recovering from exceptions and simplifying exception handling. When the operation is interrupted due to exceptions, the program's status remains unchanged; 2. Make a defensive copy of the data to be modified and modify the defensive copy of these data. The intermediate operation may raise an exception and exchange the temporary copy with the original object; 3. The target method bound to the terminator, Dispose() method and the delegate object should be ensured that they will not throw exceptions under any circumstances.
25. Master relevant tools and resources 1. Use NUnit to establish automatic unit tests (integrated in VS2010); 2. The FXCop tool will obtain the IL code in the assembly, compare it with alien encoding rules and best practices, and finally report the violations; 3. ILDasm is an IL disassembly tool that can help us understand the details; 4. SharedSourceCLI is an implementation source code that includes the .NET framework kernel and the C# compiler.
26. Avoid returning references to internal class objects 1. Since access to a value type object will create a copy of the object, the attributes of a value type will not change the state of the type object at all; 2. Constant types can avoid changing the state of the object; 3. Defining an interface restricts access to a subset to minimize the damage to the internal state of the object; 4. Defining a wrapper object to restrict access to another object; 5. Hope that when the customer code changes internal data elements, the Observer mode can be implemented so that the object can verify or correspond to the changes.
27. Replace accessible fields with attributes 1. .NET data binding only supports data binding, and using attributes can obtain the benefits of data binding; 2. In the get and set accessors of attributes, you can use lock to add multi-threaded support.
28. Using features to simplify reflection. By designing and implementing feature classes, developers are forced to use them to declare types, methods and attributes that can be used dynamically, which can reduce application runtime errors and improve software user satisfaction.
29. Defining and implementing interfaces is better than inheritance types 1. Unrelated types can jointly implement a common interface, and implementing an interface is easier than inheritance; 2. The interface is relatively stable. It encapsulates a set of functions in one interface as an implementation contract for other types, while the base class can be expanded over time.
30. Provide the ToString() method 1. It can provide user-specific information in a more friendly way; 2. Use the IFormatter.ToString() method to provide more flexible customization. If you add the IFormatProvider and ICustomFormatter interfaces, it will be more meaningful to customize the message output.
31. Use a static constructor to initialize static members 1. The static constructor will be executed before any method, variable or attribute of a class is accessed; 2. The static fields will also run before the static constructor, and the static constructor is conducive to exception handling.
32. Rewriting is better than event processor 1. If an event processor throws an exception, other processors on the event chain will not be called, and this will not happen to the rewritten virtual method; 2. Rewriting is much more efficient than associated event processors. The event processor needs to iterate over the entire request list, which takes up more CPU time; 3. Events can respond at runtime, have more flexibility, and can associate multiple responses to the same event; 4. The prevailing rule is to handle events in a derived class, which is better rewriting.
33. Understand the defects of GetHashCode() method 1. GetHashCode() only applies hash values that define keys based on hash, such as HashTable or Dictionary; 2. GetHashCode() should follow three corresponding rules: two equal objects should return the same hash code; they should be an instance invariant; the hash function should produce a random distribution among all integers.
34. Implement small-size and high-cohesive as much as possible. 1. Put all public classes and common base classes in some assembly sets, put the tool classes that provide functions for public classes into the same assembly set, package the relevant public interfaces into their own assembly sets, and finally deal with classes that are horizontally located in the application; 2. In principle, create two components: one is a small but aggregated assembly with a certain specific function, and the other is a large and wide assembly with a shared function.
35. Use events to define external interfaces 1. The event should be declared as a common event, and let the compiler create add and renmove methods for us; 2. Use the System.ComponentModel.EventHandlerList container to store each event handler. When a large number of events are included in the type, it can be used to hide the complexity of all events.
36. Avoid cast operators. Using constructors instead of conversion operators can make the conversion work clearer. Because temporary objects used after conversion can easily lead to some weird bugs.
37. Limit the visibility of types 1. Using interfaces to expose types can make it easier for us to create internal classes without limiting their availability outside the assembly; 2. The fewer public types exposed outward, the more options you have for future expansion and change implementations.
38. DataSet is better than custom structures. 1. DataSet has two disadvantages: the interaction between DataSet using XML serialization mechanism and non-.NET code is not very good; DataSet is a very general container; 2. Strongly typed DataSet breaks more design rules, and the development efficiency it obtains is far higher than the more elegant designs written by oneself.
39. Distinguish interface implementation and virtual method rewriting 1. When implementing an interface in a base class, the derived class needs to use new to hide the use of the base class method; 2. The method of the base class interface can be declared as a virtual method, and then implemented in the derived class.
40. Customize and support data binding 1. BindingMananger and CurrencyManager two objects realize data transmission between controls and data sources; 2. Advantages of data binding: using data binding is much simpler than writing your own code; it should be used for scopes outside of text data items - other display attributes can also be bound; WindowsForms data binding can handle the synchronization of multiple controls to check related data sources; 3. When the object does not support the required attributes, data binding can be supported by masking the current object and then adding a desired object.
41. Express callbacks using delegates 1. The delegate object itself does not provide any exception capture, so any multicast delegate call will end the entire call chain; 2. By displaying each delegate target on the call delegate chain, it can be avoided that the multicast delegate returns only the output of the last delegate.
42. Implement CLS-compatible assembly as much as possible 1. Create a compatible assembly requires two rules: the parameters and return value types used by all public and protected members in the assembly must be compatible with CLS; any public and protected members that are incompatible with CLS must have a CLS-compatible alternative; 2. The CLS compatibility type check can be avoided by explicitly implementing the interface, and CLSCompliantAttribute will not check the CLS compatibility of private members.
43. Use using and try/finally statements to clean up resources. Use GC.SuppressFinalize() in the Dispose() method of the IDisposable interface to notify the garbage collector that no longer performs the final operation.
44. Reasonable use of .NET runtime diagnostics 1. System.Diagnostics.Debug\Trace\EventLog provides all the tools needed for the program to add diagnostic information for the runtime. EventLog provides the application when the entry is written to the system event log; 2. Finally, do not write your own diagnostic library, .NETFCL already has the core library we need.
45. Avoid ICloneable interface 1. For value types, you never need to support ICloneable interface. Use the default assignment operation; 2. For base classes that may need to support ICloneable interface, a protected copy constructor should be created for them, and the IConeable interface should be avoided.
46. Use IComparable and IComparer interfaces to implement the sorting relationship 1. The IComparable interface is used to implement the most natural sorting relationship for types. Overload four comparison operators, and can provide an overloaded version of the CompareTo() method to allow it to accept specific types as parameters; 2. IComparer is used to provide a sorting relationship different from IComparable, or to provide us with a sorting relationship that is not implemented in the type itself.
47. Multiple representation relationships of equality judgment 1. ReferenceEquals() determines that references are equal, and two references are required to return true; 2. The static Equals() method first makes reference judgments, and then makes value type judgments; 3. For the judgment of reference types, you can use the overwrite Equals() method when using value semantics; 4. When rewriting the Equals() method, you should also rewrite the GetHashCode() method, and provide the operator==() operation.
48. Using standard configuration mechanism 1. The System.Windows.Application class of the .NET framework defines the properties for establishing a common configuration path for us; 2. Application.LocalAppDataPath and Application.userDataPath will generate path names for local data directory and user data; 3. Do not write data in ProgramFiles and Windows system directories. These locations require higher security permissions, and do not expect users to have write permissions.
49. is and as1. Both types are converted at runtime. The as operator can only be used in reference types, while is can use values and reference types; 2. The usual practice is to use is to judge the type using is, and then choose to use as or strong type conversion operators (conversion defined by operator) to selectively.