Location>code7788 >text

Two or three things that come to mind from C# delegate callbacks

Popularity:32 ℃/2024-11-15 17:30:32

Write in the front:
During the previous development process, I felt more and more that in the face of complex interface requirements, it is better to use UserControl to encapsulate the interface design of different modules individually, in order to cope with the frequent changes in customer requirements. This can be done in the face of different UI requirements, dynamically load the pre-designed module-specific UserControl, do not need to use the code of the interface to carry out complex control, otherwise you have to use the code to control the generation and display of a control. The design of the initial effort , but later maintenance is more convenient .
Background:
Recently, I developed a new tool for displaying data for different modules, and I designed different layouts individually encapsulated as UserControls, which are placed in PanelControls for data display. In order to be able to flexibly initialize the data, I subscribed each UserControl to the notification event of the main program. Using delegate callbacks, the delegate is called in the main program and the specific initialization method is automatically executed in the child control.

// Define a delegate with one parameter and no return value.
delegate void DelegateUpdateUserControl(DeviceConfig item);.
// Declare the delegate object
private DelegateUpdateUserControl UpdateUserControl; //Declare the delegate object.

Big things happen:
During testing, there was no problem with dynamic loading of data and switching of UserControl displays. However, as the number of switches increased, the interface became more and more laggy.
At this time, I was alarmed - some resources that should be released have not been released? But every time I make an interface switch, I call the Clear method to clear the object inside the PanelControl (();), at which point I began to suspect that the UserControl object is still being referenced by the main program, the system can't perform a (), and the object isn't being recycled.
Testing:
I test this by adding a line of method to the subscribe method in UserControl.
Ideally every time you switch UserControls, you would call the delegate once and have the delegate method bound to the UserControl output a message. The test however found that the first time it would output 1, the second time 2, the third time 3...
This means that except for the first call to the delegate method, there are more than one object responding to each subsequent call and the number increases sequentially, so it does seem that the UserControl is not being successfully recovered each time.
NET's garbage collection mechanism, it is concluded that the UserControl object is still being referenced by the main program, resulting in the unsuccessful release of the object, which can be attributed to the failure to unbind the delegate from the method.
I will do the following optimizations for this problem:
Modify the following code:

//Empty the old UserControl when switching controls.
//Empty the PanelControl of all controls, usually only one control exists.
().

Change to:

if ( > 0)
{
    // Get the UserControl(PanelControlContainer) object.
    UControlProperty control = (UControlProperty)[0];
    //Cancel the event mount and unbind the delegate
    UpdateUserControl -= ;)
    //remove the old UserControl from the PanelControl
    (control);
    //Actively release the resource
    ();
}

The above steps are mainly to cancel the event mount, release the reference relationship between the main program and the UserControl object, and allow the GC to recover resources in time.
Results:
After the final test, after calling the delegate, only the current latest UserControl object will respond, and the phenomenon of lag when switching interfaces disappears.

Extension - .NET garbage collection mechanism:
NET's garbage collection mechanism, we must have some understanding of .NET's garbage collection mechanism, take this opportunity to talk about the GC recovery mechanism. The following content refers to "CLR via C#" (Fourth Edition) (CLR: Common Language Runtime):

  • In C#, when we create an object, its reference is stored in the Stack and the actual data of the object is stored in the Heap. This is an automatic memory management called Garbage Collection (GC) at .NET runtime.
  • Each object has two overhead fields: the type object pointer and the synchronization block index, each taking up 8 bytes in a 64-bit application.
  • All reference-type variables are called root objects, and these root objects are the starting point for garbage collection, including local variables, global variables, static fields, and so on;
  • The CLR suspends all threads when it starts recycling, to prevent threads from accessing objects and changing their state during CLR checks;
  • The CLR then enters the marking phase of GC, where the CLR traverses all objects in the heap, setting one bit in the synchronization index block to zero;
  • The CLR then checks all active roots, and any root that references an object on the heap, the CLR flags that object, setting the bit in the synchronization index block to one;
  • After an object is tagged, the CLR checks the root of that object, tags the objects they reference, and does not recheck the object's fields if the object is already tagged to avoid causing a dead loop;
  • At the end of the check, marked objects have at least one root reference, and we say that such objects are reachable and cannot be garbage collected;
  • GC removes untagged objects, moves tagged objects to a contiguous piece of space, compresses them, and avoids space fragmentation;
  • As a compression step, the CLR also subtracts from each root the number of offset bytes in memory of the referenced object, ensuring that each root references the previous object;
  • Finally resumes execution of the previously suspended thread;

Summary:
Bind a delegate to an object event, and before releasing the object, take the initiative to unbind the delegate to avoid the problem of not being able to release the resource;
The CLR is a generation-based garbage collection mechanism that works automatically and releases resources that will not be accessed again at irregular intervals.