Fifty ways to optimize the C program

  • 2020-05-17 06:17:08
  • OfStack

1. Replace accessible fields with properties
1.NET data binding only supports data binding, and the benefits of data binding can be obtained by using properties;
2, in the property of get and set accessors can use lock to add multithreading support.

2. readonly (runtime constants) and const (compile-time constants)
1. const can only be used for primitive types, enums and strings, while readonly can be any type;
2. const will be replaced with a concrete constant at compile time, so that if both const and readonly values are used in the reference, another change to readonly will change the original design, which is to recompile the changed assembly to re-reference the new constant value.
3. const is more efficient than readonly, but it loses the flexibility of application.

3. is as
1. Both types are converted at runtime. The as operator can only be used for reference types, while is can use values and reference types.
2. The usual practice is to use is to determine the type and then choose to use either as or the strongly typed conversion operator (the conversion defined by operater) selectively.

4. ConditionalAttribute instead of #if #endif conditional compilation
1. ConditionalAttribute is only used at the method level, and it is invalid to add other types, properties, etc. #if #endif is not subject to this restriction;
2. ConditionalAttribute can add multiple compile-condition or (OR) operations, while #if #endif can add (AND) [which can be fully defined as a separate symbol here];
3. The ConditioanlAttribute definition can be placed in a single method, making the program more flexible.

5. Provide the ToString() method
1. Detailed information of users can be provided in a more friendly way;
2. Use the IFormatter.ToString () method to provide more flexible customization, and it would be more meaningful to add the IFormatProvider and ICustomFormatter interfaces to customize the message output.

6. Difference between value and reference type
1. Value types do not support polymorphism and are suitable for storing data operated by the application, while references support polymorphism and are suitable for defining the behavior of the application;
2. Defining the array as a value type 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 carried out in the way of replication to avoid exposing the internal structure to the outside world;
4. Value types are used in the following scenarios: types are mainly used for data storage; The public interface is completely defined by some data member access properties; There's never a subclass; There will never be polymorphic behavior.

7. Value types are implemented as constant and atomic types as possible
1. Make our code easier to write and maintain;
2. Three strategies for initializing constants: in construction; Factory method; Construct a mutable helper class (such as StringBuilder).

8. Make sure that 0 is a worthy valid state
1. The default state of the value type should be 0;
2, the enumeration type of 0 should not be invalid state; In FlagsAttribute is to ensure that the value of 0 is in a valid state;
3. When the string is empty, an empty string of string.Empty can be returned.

9. Multiple representation relationships for equality judgments
1. ReferenceEquals() to judge that the references are equal, true can be returned only if two references are the same object.
2. The static Equals() method first makes reference judgment, and then makes value type judgment;
3. The reference type can be determined by using the override Equals() method when using the value semantics;
4. When you override the Equals() method, you should also override the GetHashCode() method, providing the operater==() operation.

10. Understand the drawbacks of the GetHashCode() method
1. GetHashCode() only applies to hash values of ** defined keys based on hash, such as HashTable or Dictionary;
2. GetHashCode() should follow the corresponding three rules: two equal objects should return the same hash code; It should be an instance invariant; The hash function should generate a random distribution of all integers.

101. foreach looping is preferred
1. foreach can eliminate the compiler's checking of the for loop on array boundaries;
2. The loop variable of foreach is read-only, and there is an explicit conversion to throw an exception when the object type of ** object is incorrect;
3. ** used by foreach shall include: public GetEnumberator() method; The IEnumberable interface is explicitly implemented; The IEnumerator interface is implemented.
4. foreach can bring resource management benefits, because if the compiler can determine the IDisposable interface, it can use the optimized try... finally block;

102. Initialization of default fields is superior to assignment statements
1. By default, the field life initializes the value type to 0, and the reference type to null;
2. Multiple initializations of the same object will reduce the execution efficiency of the code;
3. Initializing a field into a constructor is good for exception handling.

Initialize a static member with a static constructor
1. The static constructor is executed before any method, variable, or property of a class is accessed;
2. Static fields also run before static constructors, and static constructors are good for exception handling.

104. Using the constructor chain (this problem has been solved with optional parameters in.NET 4.0)
1. Assign initialization to another constructor with this, and call the constructor of the base class with base;
2. The operation order of type instance is: set all static fields to 0; Execute the static field initializer; Executes the static constructor for the base class; Executes the static constructor for the current type;
Set all the instance fields to 0; Execute the instance field initializer; Execute the appropriate base class instance constructor; Executes the instance constructor for the current type.

105. Clean up resources using using and try/finally statements
GC.SuppressFinalize () is used in the Dispose() method of the IDisposable interface to notify the garbage collector that it will no longer terminate.

Minimize memory garbage
1. Allocating and destroying objects on a heap takes extra processor time;
2. Techniques to reduce the number of allocated objects: frequently used local variables are promoted to fields; Provides a class for storing Singleton objects to represent common instances of a particular type.
3. Perform complex string operations with StringBuilder.

Minimize crating and unpacking
1. Pay attention to the implicit conversion of a type to System.Object, and the value type should not be replaced with System.Object;
2. You can avoid boxing by using an interface instead of a type, which implements the value type from the interface and then calls members through the interface.

108. Implement the standard Dispose pattern
1, the use of memory resources, it must be a finalizer, the garbage collector after the finish has not put an end to its memory object, will realize the finalizer object is added to the end of the queue, and then the garbage collector will start a new thread to run these objects on the finalizer, this way of defensive into because if users forget call Dispose () method, the garbage collector will always call a finalizer method, so that you can avoid unmanaged memory resources are not released cause memory leak problem;
2. Using the IDisposable.Dispose () method requires four things: releasing all unmanaged resources; Release all managed resources; Set a status flag to indicate whether Dispose() has been executed; Call GC.SuppressFinalize (this) to cancel the terminal operation of the object;
3. Add a protected virtual method, Dispose(), to the type that requires polymorphism. The derived class releases its task by overriding this method.
4. In the type that requires the IDisoposable interface, we should implement one finalizer even if we don't need one.

109. Define and implement interfaces over inheritance types
1. Unrelated types can jointly implement a common interface, and it is easier to implement the interface than inheritance;
2. The interface is relatively stable. It encapsulates a group of functions in an interface as a contract for other types of implementation, while the base class can be extended over time.

210. Discernible interface implementation and virtual method rewrite
1. When implementing an interface in the base class, the derived class needs to use new to hide the use of the base class methods;
2. You can declare the methods of the base class interface as virtual methods and then implement them in the derived class.

2101. Using a delegate to express a callback
1. The delegate object itself does not provide any exception capture, so any multicast delegate call will end the whole call chain;
2. Avoid the multicast delegate returning only the output of the last delegate by displaying the call to each delegate target on the delegate chain.

Define external interfaces using events
1, should be declared as a common event, let the compiler for us to create add and renmove methods;
2. Use the System.ComponentModel.EventHandlerList container to store event handlers, which can be used to hide the complexity of all events when the type contains a large number of events.

Avoid returning references to inner class objects
1. Since the access of a value type object will create a copy of the object, defining the properties of a value type will not change the internal state of the type object at all;
2. Constant types can avoid changing the state of objects;
3. Define the interface to limit access to 1 subset to minimize damage to the internal state of the object;
4. Define one wrapper object to restrict access to another;
5. You want the Observer pattern to be implemented when the customer code changes internal data elements so that the object can validate or respond to the changes.

Declarative programming is better than imperative programming
You can avoid the possibility of errors in multiple similar hand-written algorithms and provide clear and readable code.

Implement types as serializable as possible
1. Type does not represent UI controls, Windows, or forms, and should be serialized;
2. When adding the deserialized property of NonSerializedAttribute, the default value can be loaded by implementing IDeserializationCallback's OnDeserialization() method;
3. The ISerializable interface can be used for flexible control in version control. Meanwhile, a serialized constructor is provided to initialize objects according to the data in the stream.
4. If you need to create a derived class, you need to provide a hook method for the derived class to use.

Use the IComparable and IComparer interfaces to implement the ordering relationship
1. The IComparable interface is used to realize the most natural sorting relationship for types. By overloading four comparison operators, it can provide an overloaded version of CompareTo() method to accept specific types as parameters.
2. IComparer is used to provide ordering relationships that are different from IComparable, or to provide us with ordering relationships that are not implemented by the type itself.

Avoid ICloneable interfaces
1. For value types, ICloneable interface should never be supported, just use the default assignment operation;
2. For base classes that may need to support the ICloneable interface, create a protected copy constructor and avoid supporting the IConeable interface.

Avoid casting operators
The conversion can be made clearer by using a constructor instead of the conversion operator, which can easily lead to a weird BUG because of the temporary objects used after the conversion.

2109. Use of the new modifier is only considered if the accumulation of new editions causes problems

310. Implement CLS compatible assemblies whenever possible
1. To create a compatible assembly, two rules must be followed: all public and protected members of the assembly must use parameters and return value types that are compatible with CLS; Any public and protected member that is not compatible with CLS must have an CLS compatible alternative;
2. CLS compatibility type checking can be avoided by explicitly implementing the interface, and CLSCompliantAttribute does not check for CLS compatibility of private members.

3101. Implement short and concise methods whenever possible
1. The JIT compiler compiles methods as a unit, and methods that are not called will not be compiled by JIT.
2. If the code of the Case statement in the longer Switch is replaced with one method at a time, the time saved by the JIT compiler will be multiplied;
3. The use of registers can be optimized by using a short and concise method and selecting fewer local variables;
4. The fewer control branches in the method, the easier it is for the JIT compiler to put variables into registers.

3102. Implement small, highly cohesive assemblies whenever possible
1. Put all public classes and common base classes in one assembly, put utility classes that provide functionality for the public classes in the same assembly, package related public interfaces into their own assembly, and finally process classes that are distributed horizontally throughout the application;
2. In principle, create two types of components: one that is small and aggregated, with a specific function, and one that is large and wide, with Shared functions.

Restrict visibility of types
1. Using interfaces to expose the capabilities of types makes it easier to create inner classes without limiting their availability outside the assembly;
2. The fewer public types you expose, the more options you have for future extensions and change implementations.

Create large-grained Web API
This minimizes the frequency and load of transactions between machines, placing large operations and fine-grained execution on the server.

Overrides over event handlers
1. If an event handler throws an exception, other handlers on the event chain will not be called, and the rewritten virtual method will not be called.
2. Rewriting is much more efficient than associating event handlers, which need to iterate through the entire request list, thus taking up more CPU time;
3. Events can respond at runtime with more flexibility, and multiple responses can be associated with one event;
4. The prevailing rule is to handle the event of a derived class in a better way.

Rational use.NET runtime diagnostics
1. System. Diagnostics. Debug\Trace\EventLog provides all the tools needed to add diagnostic information to the program at runtime.
2. Finally, don't write your own diagnostic library.NET FCL already has the core library we need.

Use the standard configuration mechanism
1. The System.Windows.Application class of the NET framework defines the properties for us to establish the common configuration path;
2. Application.LocalAppDataPath and Application.userDataPath will generate the pathnames of local data directories and user data;
3. Do not write data to ProgramFiles and Windows system directories. These locations require higher security permissions and do not expect users to have write permissions.

Customize and support data binding
1. The two objects BindingMananger and CurrencyManager realize the data transmission between the control and the data source;
2. Advantages of data binding: it is much easier to use data binding than to write your own code; It should be used outside the scope of the text data item -- other display properties can also be bound; For the Windowos Forms data binding can handle multiple control synchronization check related data sources;
3. When an object does not support the required properties, it can support data binding by masking the current object and then adding a desired object.

Verify with.NET
1. ASP. NET has 5 controls to verify the validity. You can use CustomValidator to derive a new class to add your own authenticator;
2, Windows validation need System. Windows. Forms. Control. Validating write an event handler.

410. Select the appropriate ** as needed
1. Arrays have two obvious defects: they cannot be dynamically resized; Resizing is time consuming;
2. ArrayList mixes the features of 1-dimensional arrays and linked lists. Queue and Stack are special arrays based on Array.
3, when the program is more flexible to add and remove items, can make more robust ** type, when creating a simulated ** class, it should be implemented for its indexer and IEnumberable interface.

DataSet is better than a custom structure
1. DataSet has two disadvantages: the interaction between DataSet using the XML serialization mechanism and the non-NET code is not very good; DataSet is a very versatile container;
2. The strongly typed DataSet breaks more design rules and achieves far more development efficiency than the more elegant design it writes.

Simplify reflection with features
By designing and implementing feature classes and forcing developers to use them to declare types, methods, and properties that can be used dynamically, you can reduce application runtime errors and improve software user satisfaction.

Avoid overusing reflexes
1. The parameters and return values used by Invoke members are System. Object.
2. The interface allows us to get a cleaner and more maintainable system. Reflection is a powerful late binding mechanism, which is used by the.NET framework to implement data binding of the Windows control and Web control.

Create a specific exception class for your application
1. The only reason for needing different exception classes is to make it easy for users to write catch processors to do different things for different errors;
2. When there may be different repair behaviors, we should create a variety of different exception classes. By providing all constructors supported by the exception base class, we can create fully functional exception classes for the application.

Select exception security first
1. Strong exceptions ensure the best balance between recovering from exceptions and simplifying exception handling. When the operation is interrupted due to exceptions, the state of the program remains unchanged;
2. Make a defensive copy of the data to be modified, and modify the defensive copy of the data. The operation in the middle may cause an exception and exchange the temporary copy with the original object;
3. The finalizer, Dispose() method, and the target method bound by the delegate object should in any case ensure that they do not throw an exception.

Minimize interoperability
1. Interoperability has three costs: the enumeration cost of data between managed heap and unmanaged heap, the cost of switching between managed code and unmanaged code, and the development work for developers dealing with a mixed environment;
2. The blittable type in interop can be effectively copied back and forth between managed and unmanaged environments without being affected by the internal structure of the object;
3. Use the In/Out feature to ensure the most appropriate unnecessary multiple copies, and improve performance by declaring how the data is enumerated;
4. Use COM Interop to interoperate with the COM component in the simplest way, use P/Invoke to call Win32 API, or use the C++ compiler /CLR switch to mix managed and unmanaged code;

Select security code first
1. Avoid accessing unmanaged memory as much as possible. Isolated storage cannot prevent access from managed code and trusted users;
2. Consider using isolated storage when assemblies are running on Web, and when certain algorithms do require higher security permissions, isolate that code in a separate assembly.

Master relevant tools and resources
1. Set up automatic unit tests using NUnit (integrated in VS2010);
2. The FXCop tool will obtain the IL code in the assembly, compare it with the heterogeneous coding rules and best practices, and finally report the violation;
3. ILDasm is an IL disassembly tool, which can help us gain insight into details.
4, Shared Source CLI is a framework containing.NET kernel and C# compiler implementation source code.

4109. Prepare for C#2.0 (this rule doesn't make any sense now that we're at 4.0)
510. Knowledge of ECMA standard


Related articles: