This article is a mirror article of machine translation, please click here to jump to the original article.

View: 20516|Reply: 0

[Tips] Forty-seven ways to optimize a C# program

[Copy link]
Posted on 3/15/2018 10:41:59 AM | | |

1. Replace accessible fields with attributes

1、. .NET data binding only supports data binding, and you can get the benefits of data binding by using attributes.
2. In the get and set access to the property, you can use lock to add multi-threading support.

2. readonly (runtime constant) and const (compile-time 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 a specific constant at compile time, so that if both const and readonly values are used in the reference, the change to readonly will change the original intention of the design, which is the need to recompile the changed assembly to rereference the new constant value.
3. const is more efficient than readonly, but loses the flexibility of application.

3. IS and AS

1. Both are type conversions at runtime, as operators can only be used in 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 as or a strong type conversion operator (conversion defined by an operater) selectively.

4. ConditionalAttribute instead of #if #endif条件编译

1. ConditionalAttribute is only used at the method level, and other items such as types, attributes, etc. are invalid. And #if #endif则不受此限制;
2. ConditionalAttribute can add multiple OR (OR) operations for compilation conditions, and #if #endif则可以添加与(AND) [here can be completely defined as another separate symbol];
3. The ConditioanlAttribute definition can be placed in a separate method to make the program more flexible.

5. Provide the ToString() method

1. It can provide detailed information to users in a more friendly way;
2. Use the IFormatter.ToString() method to provide more flexible customization, and if you add the IFormatProvider and ICustomFormatter interfaces, it will make more sense to customize the message output.

6. The difference between value and reference type

1. Value types do not support polymorphism, which is suitable for storing data operated by applications, while references support polymorphism, which is suitable for defining application behavior.
2. For arrays defined as value types, the performance of the program can be significantly improved;
3. The value type has less heap memory fragmentation, memory garbage and indirect access time, and its return in the method is done in the form of replication to avoid exposing the internal structure to the outside world.
4. Value types are used in the following scenarios: The responsibilities of types are mainly used for data storage; Public interfaces are completely defined by some data member access attributes; There are never subclasses; There is never polymorphic behavior.

7. Value types should be 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; plant method; Construct a mutable helper class (e.g. StringBuilder).

8. Ensure that 0 is worthy of valid status

1. The default state of the value type should be 0;
2. The 0 of the enum type should not be invalid; In the FlagsAttribute is to ensure that the 0 value is valid state;
3. When the string is empty, a string can be returned. empty string for empty.

9. Multiple representation relationships of equal judgment

1. ReferenceEquals() determines that the references are equal, and it needs to be true when both refer to the same object.
2. The static Equals() method is used to make reference judgment first, and then to judge the value type;
3. For the judgment of the reference type, you can use the rewrite Equals() method when using value semantics.
4. When rewriting the Equals() method, the GetHashCode() method should also be rewritten, and the operater==() operation should be provided at the same time.

10. Understand the shortcomings of the GetHashCode() method

1. GetHashCode() is only applied to hash values of hash-based ** defined keys, such as HashTable or Dictionary;
2. GetHashCode() should follow the corresponding three rules: two equal objects should return the same hash code; should be an instance invariant; The hash function should produce a random distribution across all integers.

11. Give priority to the use of foreach loop statements

1. foreach can eliminate the compiler's check of the array boundary of the for loop;
2. The circular variable of foreach is read-only, and there is an explicit transformation, which throws an exception when the object type of the ** object is incorrect;
3. The ** required to use foreach is: have the public GetEnumberator() method; The IEnumberable interface is explicitly implemented. IEnumerator interface is implemented;
4. foreach can bring the benefits of resource management, because if the compiler can determine the IDisposable interface, it can use the optimized try... finally block;

12. The initialization of the default field is better than the assignment statement

1. The field life will initialize the value type to 0 and the reference type to null by default.
2. Initializing the same object multiple times will reduce the execution efficiency of the code.
3. Putting the initialization of the field in the constructor is conducive to exception handling.

13. Use the 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. Static fields will also run before the static constructor, and the static constructor is conducive to exception handling.

14. Use the constructor chain (in. NET 4.0 already solves this problem with optional parameters)

1. Use this to hand over the initialization work to another constructor, and use base to call the constructor of the base class;
2. The operation sequence of type instances is: set all static fields to 0; Execution of static field initializers; a static constructor that executes the base class; Static constructors that execute the current type;
Set all instance fields to 0; Execute instance field initializers; Execute the appropriate base class instance constructor; Execute the instance constructor of the current type.

15. Use using and try/finally statements to clean up resources

In the Dispose() method of the IDisposable interface, GC.SuppressFinalize() can be used to notify the garbage collector that the final operation is no longer performed.

16. Minimize memory garbage

1. It takes extra processor time to allocate and destroy objects on a heap;
2. Techniques for reducing the number of assigned objects: frequently used local variables are promoted to fields; Provides a class that stores common instances of Singleton objects that express specific types.
3. Use StringBuilder to perform complex string operations.

17. Minimize packing and unpacking

1. Pay attention to the implicit conversion of a type to System.Object, and the value type should not be replaced with the System.Object type;
2. Using interfaces instead of types can avoid boxing, that is, implementing value types from interfaces, and then calling members through interfaces.

18. Implement the standard Dispose mode

1. To use non-memory resources, it must have a finalizer, the garbage collector will add the implemented finalizer objects to the termination queue after completing the memory objects that have not terminated them, and then the garbage collector will start a new thread to run the finalizers on these objects. This can avoid the problem of memory leakage caused by unmanaged memory resources not being released.
2. Using the IDisposable.Dispose() method requires four aspects of work: releasing all unmanaged resources; Free up all managed resources; Set a status marker 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() to the type that needs polymorphism, and the derived class releases its task by rewriting this method.
4. In the type that requires an IDisoposable interface, we should implement a terminator even if we don't need one.

19. Define and implement interfaces over inheritance types

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

20. Distinguish between interface implementation and virtual method rewriting

1. When implementing an interface in the 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.

21. Use entrustment to express callbacks

1. The delegate itself does not provide any exception capture, so any multicast delegate call will end the entire call chain.
2. By displaying and calling each delegation target on the delegate chain, you can avoid multicast delegates returning only the output of the last delegate.

22. Use events to define external interfaces

1. It 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, and use it to hide the complexity of all events when the type contains a large number of events.

23. Avoid returning references to internal class objects

1. Since the access of a value type object will create a copy of the object, the attributes of defining a value type will not change the state inside the type object at all;
2. Constant types can avoid changing the state of the object;
3. Define the interface to limit access to a subset to minimize the damage to the internal state of the object.
4. Define a wrapper object to restrict access to another object;
5. When the customer code changes the internal data elements, the Observer mode can be implemented, so that the object can verify or correspond to the changes.

24. Declarative programming is better than imperative programming

The possibility of making mistakes in multiple similar hand-written algorithms can be avoided and clear and readable code is provided.

25. Implement types as serializable as possible

1. The type represents not a UI control, window or form, and the type should support serialization;
2. When adding the deserialized attribute of NonSerializedAttribute, the default value can be loaded by the OnDeserialization() method that implements IDeserializationCallback;
3. In version control, you can use the ISerializable interface for flexible control, and provide a serialization constructor to initialize objects according to the data in the stream, and also require the permission of SerializationFormatter exceptions when implementing.
4. If you need to create a derived class, you need to provide a hook method for the derived class.

26. Use IComparable and IComparer interfaces to implement sorting relationships

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

27. Avoid ICloneable interfaces

1. For value types, there is no need to support ICloneable interface, just use the default assignment operation;
2. For base classes that may need to support ICloneable interfaces, a protected replication constructor should be created for them, and IConeable interfaces should be avoided.

28. Avoid forced conversion operators

Using constructors instead of conversion operators can make the conversion work clearer, which can easily lead to some weird bugs due to temporary objects used after conversion.

29. Only consider using the new modifier when the accumulation of new versions causes problems

30. Implement CLS-compatible assemblies as much as possible
1. To create a compatible assembly, two rules need to be followed: the parameters and return value types used by all public and protected members of the assembly must be compatible with CLS; Any public and protected member that is not compatible with the CLS must have a CLS-compatible alternative;
2. You can bypass the CLS compatibility type check by explicitly implementing the interface, and the CLSCompliantAttribute will not check the CLS compatibility of private members.

31. Implement a short and concise method as much as possible

1. The JIT compiler compiles in units of methods, 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. Short and concise methods 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.

32. Realize small size and high cohesive assemblies as much as possible

1. Put all public classes and common base classes into some assemblies, put the tool classes that provide functions for public classes into the same assembly, package the relevant public interfaces into their own assemblies, and finally process the classes that are all over the horizontal position in the application;
2. In principle, two types of components should be created: one is a small and aggregated assembly with a specific function, and the other is a large and wide assembly with common functions.

33. Limit the visibility of types

1. Using interfaces to expose the functions of types can make it easier for us to create internal classes without limiting their availability outside the assembly;
2. The fewer public types exposed to the outside world, the more options you have for future expansion and change implementations.

34. Create a large-granular Web API

This minimizes the frequency and load of transactions between machines, putting large operations and fine-grained executions to the server.

35. Rewriting is better than event processors

1. If an event processor throws an exception, other processors on the event chain will not be called, but this will not happen to the rewritten virtual method.
2. Rewriting is much more efficient than associative event processors, which need to iterate over the entire request list, which takes up more CPU time.
3. Events can be responded to at runtime, with more flexibility, and multiple responses can be associated with the same event.
4. The common rule is to deal with a derived event, and the rewriting method is better.

36. Fair use. .NET runtime diagnostics

1. System.Diagnostics.Debug\Trace\EventLog provides all the tools needed for the program to add diagnostic information to the runtime, and the application can write to the system event log when the EventLog provides the ingredient;
2. Finally, don't write your own diagnostic library, .NET FCL already has the core library we need.

37. Use standard configuration mechanisms

1、. The System.Windows.Application class of the .NET framework defines the properties for us to establish a common configuration path;
2. Application.LocalAppDataPath and Application.userDataPath will generate the path names of the local data directory and user data;
3. Do not write data in ProgramFiles and Windows system directories, these locations require higher security permissions, do not expect users to have write permissions.

38. Customize and support data binding

1. The two objects of BindingMananger and CurrencyManager realize the data transfer between the control and the data source;
2. Advantages of data binding: using data binding is much simpler than writing your own code; It should be used for scopes other than text data items - other display properties can also be bound; For Windowos Forms data bindings, the ability to handle multiple control synchronization of check-related data sources;
3. When the object does not support the required attributes, you can support data binding by blocking the current object and then adding the desired object.

39. Use. .NET validation

1. There are five controls in the ASP.NET to verify validity, and you can use CustomValidator to derive a new class to add your own authenticator.
2. Windows validation requires a sub-System.Windows.Forms.Control.Validating to write an event handler.

40. Choose the appropriate ** according to the needs

1. The array has two obvious defects: it cannot be dynamically resized; Resizing is time-consuming;
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 is more flexible to add and delete items, it can make more robust types, and when creating a class that simulates **, it should implement indexers and IEnumberable interfaces for it.

41. DataSet is better than custom structure

1. DataSets have two disadvantages: the interaction between DataSets using XML serialization mechanism and non-.NET code is not very good; DataSet is a very versatile container;
2. Strong types of DataSets break more design rules, and their development efficiency is much higher than that of the more elegant designs written by themselves.

42. Use characteristics to simplify reflection

By designing and implementing feature classes that force developers to declare dynamically usable types, methods, and attributes, you can reduce application runtime errors and improve software user satisfaction.

43. Avoid overusing reflexes

1. The parameters and return values used by Invoke members are System.Object, which converts types at runtime, but the possibility of problems has become more likely.
2. The interface allows us to get a clearer and more maintainable system, and reflection is a very powerful late binding mechanism. .NET framework uses it to implement data binding for Windows controls and web controls.

44. Create specific exception classes for the application

1. The only reason why different exception classes are needed is to allow users to easily take different approaches to different errors when writing catch processors;
2. When there may be different repair behaviors, we should create a variety of different exception classes, by providing all the constructors supported by the exception base class, we can create a fully functional exception class for the application, and use the InnerException attribute to save all the error information generated by lower-level error conditions.

45. Give priority to abnormal safety guarantees

1. Strong exception guarantee provides the best balance between recovery from exception and simplified exception handling, and the state of the program remains unchanged when the operation is interrupted due to the exception.
2. Make defensive copying of the data to be modified, modify the defensive copy of these data, the operation in the middle may cause an exception, and the temporary copy and the original object will be exchanged;
3. Terminators, Dispose() methods, and target methods bound to delegates should ensure that they do not throw exceptions under any circumstances.

46. Minimize interoperability

1. There are three costs of interoperability: the cost of data enumeration between managed and unmanaged heaps, the cost of switching between managed code and unmanaged code, and the development work of developers dealing with hybrid environments;
2. Using the blittable type in interop can effectively replicate 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 replications, and improve performance by declaring how the data is enumerated.
4. Use COM Interop to implement interoperability with COM components in the simplest way, use P/Invoke to call Win32 API, or use the /CLR switch of the C++ compiler to mix managed and unmanaged code;

47. Give priority to safety codes

1. Avoid accessing unmanaged memory as much as possible, and isolated storage cannot prevent access from managed code and trusted users.
2. When assemblies run on the web, consider using isolated storage, and when certain algorithms do require higher security permissions, those codes should be isolated in a separate assembly.

48. 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, analyze it against the heterogeneous coding rules and best practices, and finally report the violation.
3. ILDasm is an IL disassembly tool that can help us gain insight into details;
4. Shared Source CLI is an implementation source code that contains the .NET framework kernel and C# compiler.




Previous:Service Fabric - Stateful Service concept
Next:.net/c# SynchronizationContext for details
Disclaimer:
All software, programming materials or articles published by Code Farmer Network are only for learning and research purposes; The above content shall not be used for commercial or illegal purposes, otherwise, users shall bear all consequences. The information on this site comes from the Internet, and copyright disputes have nothing to do with this site. You must completely delete the above content from your computer within 24 hours of downloading. If you like the program, please support genuine software, purchase registration, and get better genuine services. If there is any infringement, please contact us by email.

Mail To:help@itsvse.com