Location>code7788 >text

C#.Net Foundation - Demystifying Delegates and Events

Popularity:754 ℃/2024-08-05 08:12:34

Delegation and events are relatively old techniques in C#, starting with theC#1.0The core role is to pass and use methods as parameters (variables). One of the delegate is the foundation, need to master, programming commonly used in the Lambda expression, Action, Func are delegates, including events are also based on the realization of the delegate.


01, recognize delegate delegate

1.1 What is a commission?

Delegate is a special type used to wrap methods to be passed and called as objects, similar to function pointers.delegate The keyword is used to define a delegate type, the syntax is similar to the method declaration, can be regarded as a "method signature template", and the same as the method defines the return value of the method, parameters.

  • expense or outlaydelegate The defined delegate isa classThe "method name" is the name of the delegate type.
  • Delegates are used in the same way as other common types, with the instance pointing to a reference to a method whose declaration matches the "method signature template" of the delegate definition (Support for covariant inverters)。
  • Delegates support linking multiple delegates (methods), called multicast delegates (MulticastDelegate), which are called when executed.
public delegate void Foo(string name); //assert a delegate type
void Main()
{
    Foo faction; //Declare a Foo delegate (instance) variable.
faction = DoFoo; //Assign a method
faction += str => { ($"gun {str}"); }; // Add multiple "method instances".
    faction += DoFoo; // continue adding, repeatable
faction("sam"); //Execute the delegate, multiple methods will be executed sequentially.
    ("zhang"); // Same as above, the above call is still an Invoke method.
}
private void DoFoo(string name){
($"hello {name}"); }
}

The main usage scenario of delegate: the core is to pass the method as a parameter, separating the method declaration and method implementation.

  • callback method, wrapping methods as delegates and passing them as arguments decouples method assertion, implementation, and invocation, which can be done in different places.
  • Lambda expressionThis is a simplified syntactic form of delegation, which is more concise and more commonly used.
  • event, events are a special kind of delegates, which are implemented based on delegates and can be seen as an encapsulation of delegates.

1.2、Delegate API

🔸Delegate properties clarification
Method Gets information about the method represented by the delegate, returning the last of multiple values.
Target Get the object instance to which the delegate method belongs, multiple values return the last one, and for static methods thenull
So be careful.: Delegates and events should be removed when they are not used to avoid GC failing to release resources.
🔸Delegate static member -
CreateDelegate Create a delegate of the specified type in code, including multiple overloaded methods
Combine(Delegate, Delegate) Combine multiple delegates into a new delegate (chain) to simplify syntax++=Foo d = d1 + d2;
Remove(source, value) Removes the call list of the specified delegate and returns the new delegate. Simplified syntax--=d -= d1
RemoveAll(source, value) As above, the difference isRemovevalue removes the last one found.RemoveAll Remove all found
🔸MulticastDelegate Member -
GetInvocationList() Returns a list of the delegates of this multicast delegate in order of invocation

1.3 Decryption delegate "type"

expense or outlaydelegate defined delegate, the compiler will automatically generate a sealed class, SO, the delegate is essentially a class. The delegate class inherits fromMulticastDelegatehaving also inherited fromDelegate is the base class for delegates, and they are both abstract classes.

delegateThe compiled IL code of the defined delegate is as follows (simplified) and can be viewedOnline sharplab

public delegate void Foo(string name,int age); //Declaring a delegate type

//Compiler-generatedFoocommissioning class(Simplified Code)
class public auto ansi sealed Foo extends []]
{
    void Foo(object obj, IntPtr method) { ... }
    public virtual void Invoke (string name,int32 age) { ... }
    public virtual BeginInvoke (string name,int32 age, callback, object 'object') { ... }
    public virtual void EndInvoke (class [] result) { ... }
}
  • The constructor of the delegate has two parameters, theobjis the object on which the method resides.methodis a method pointer. This constructor is called by the compiler, just understand it.
  • Three ways to execute a delegateInvokeBeginInvoke cap (a poem)EndInvoke Signature and proxy affirmation are consistent.
  • To execute a delegate (method) is to call the()The simplified syntax isfoo()BeginInvoke cap (a poem)EndInvokefor asynchronous calls.
  • Because a delegate is essentially a class, the definition of a delegate is usually external to the class (on a level with the class).

📢 Delegation, event execution, recommended use?.Invoketo determine if thenullfoo?.Invoke()

Test the inheritance hierarchy of a delegate:

public delegate void Foo(string name); //Declaring a delegate type
void Main()
{
    Foo faction; //Affirming aFoodelegate variable
	faction = DoFoo; //assign a value to something
	
	var ftype = ();
	while (ftype != null)
	{
		();
		ftype = ;
	}
	//exports:
	//Foo
	//
	//
	//
}
private void DoFoo(string name){
	($"hello {name}");
}

1.4、MulticastDelegate

The delegates and events we use in our coding are really allMulticastDelegate, which can contain multiple (single) commissions.MulticastDelegate There is a chain of delegates in the_invocationList, which can hold multiple (single) delegates (which can be added repeatedly). When a delegate is executed, the delegate methods in the delegate chain table are executed sequentially.

🔸 Add Remove: Recommended+-operator adds and removes delegates, which is essentially a call to theDelegatestatic method

📢take note of: Delegate the method of+-is thread-unsafe, the eventaddremoveis thread-safe.

🔸 Enforcement Mandate ()/A(),: all (delegated) methods are executed. This can be accomplished with theGetInvocationList() Get a list of delegates (methods) to manually control execution.

  • If the execution of one of the methods reports an error, the later ones in the chain table will not be executed.
  • If the delegate method has a return value, you can only get the last result.

📢take note of: The add and remove operations return a new delegate, and the original delegate is not affected.The commission is constant

public delegate void Foo(string name); //assert a delegate type
void Main()
void Main()
Foo f1 = default; // Declare a Foo delegate variable.
f1 += DoFoo; //add a method
f1 += DoFoo; //add another method
f1 += str => { ($"gun {str}"); }; //continue adding
f1("sam"); //execute the method 3 times
f1 -= DoFoo; //removed
f1("sam"); //executed method 2 times

Foo f2 = DoFoo; //Remove
Foo f3 = f1+f2; //combined delegate
Foo f4 = (Foo)(f1,f2); //same as above
(f3==f4); //True, same delegate if the elements in the internal method list are the same
(f3-f2 == f1); //True, remove delegation
}
private void DoFoo(string name)
{
($"hello {name}"); }
}

1.5. Anonymous Methods and Lambda Expressions

  • anonymous methodis a method that has no name (name), using thedelegateKeyword assertion that can be passed to a delegate or Lambda expression.
  • Lambda expressionLike anonymous methods, they are essentially delegates, and the generated IL code is similar.Lambda expressions are more concise and support type inference, so they are basically used in modern programming.
public delegate void Foo(string name); //Declaring a delegate type
void Main()
{
	//anonymous method
	Foo f1 = delegate(string name){
		(name);
	};
	Action a1 = delegate() { ("hello");};
	f1("sam");
	a1();
    
	//Lambdadisplayed formula
	Foo f2 = name=>(name);
	f2("king");
}

Anonymous methods, Lambda methods Will be compiled as a private method in a private class.


02、Built-in delegate type Action, Func

From the above it can be seen that the delegate will create a type at compile time, in order to improve performance, efficiency and avoid a large number of unnecessary repetitive delegate definitions, the.NetThere are some built-in generic delegatesActionFunc, which basically satisfies most common scenarios.

  • Action: Delegates supporting 0 to 16 generic parameters with no return value.
  • Func: Supports 0 to 16 input generic parameters, and one generic delegate with a return value.
  • Predicatebool Predicate<in T>(T obj)The delegate used to test the judgment returns the result of the test.bool

Source Code:

public delegate void Action();
public delegate void Action<in T>(T obj);
public delegate void Action<in T1, in T2>(T1 arg1, T2 arg2);
...
public delegate TResult Func<out TResult>();
public delegate TResult Func<in T, out TResult>(T arg);
public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);
...
public delegate bool Predicate<in T>(T obj);

upstream delegate parameterinoutare modifiers that mark mutability (covariance, inversion), as detailed later inGeneralized T & Covariant Inversion


03、Awareness EventEvent

3.1 What is an eventevent

eventis a special type of delegate, he is based on the delegate implementation, is a further encapsulation of the delegate, so the use is similar to the delegate. Events useeventkeyword is asserted, any other component can subscribe to the event, and when the event is triggered, it calls all the delegates (methods) that have subscribed to it.

Events are a delegate-based (event-driven) programming model for implementing a publish-subscribe based notification mechanism between objects is one way to implement the observer pattern. Commonly used in GUI programming, asynchronous programming, and other systems that require message-based systems.

void Main()
{
	var u = new User();
	//Subscribe to events
	 += (sender, e) => { (sender); };
	(100);
	(200);
}
public class User
{
	public int Score { get; private set; }

	public event EventHandler ScoreChanged; //Defining Events,Using the built-in“event”commission EventHandler

	public void AddScore(int score)
	{
		 += score;
		?.Invoke(this, null); //触发event
	}
}

🔸 Key players in the event

  • ① Publisher of the event, the owner of the published event, triggers the event at the right time, and passes information through the event parameters:

    • sender: The event source, the publisher who triggered the event.
    • EventArgs: event parameters, generally inherited fromobject, which of course is not required in the.NET CoreThe event parameter in can be of any type. It's just an emptyclassNothing.
  • ② Subscribers to the event: Subscription to a published event, with specific actions performed after the event occurs.

📢 EventHandler(object? sender, EventArgs e)、EventArgs<T>Sort of Microsoft's standard event pattern, a customary convention.

🔸 Event Usage Practices

  • utilization+= Subscribe to events, with support for any number of subscriptions.-=Remove unused event subscriptions to avoid memory overflow, note that the-=Doesn't work for anonymous methods, Lambda, because it's a new delegate each time.
  • Events are triggered with judgmentnullto avoid triggering an error when there is no subscription:Progress?.Invoke()
  • Event delegate types end in "EventHandler" and are used in most scenarios.EventHandler<TEventArgs>Simply, of course, customize it, or useAction

🔸 event naming: noun + verb (passive)

  • The incident has occurred withpast tense:Closed、PropertyChanged。
  • The event is about to occur using thepresent tense,Closing、ToolTipOpening。
  • Subscribed methods are usually prefixed with "On”、“Raise”, += OnProgress;

3.2. Decryption events - "encapsulated delegates"

Event Definition:public event EventHandler MyEvent;whichEventHandleris a delegate, the source code of which is below:

public delegate void EventHandler(object? sender, EventArgs e);
public delegate void EventHandler<TEventArgs>(object? sender, TEventArgs e);

When an event is defined, the C# compiler generates an event wrapper around the delegate, similar to the way properties are wrapped around fields, online.sharplabSource Code.

//Define an event
public event EventHandler MyEvent;
//define an event with other delegates
public event Action<string> MyEvent2.

//compiled IL code (simplified) **********

// Delegate fields
private EventHandler m_MyEvent;
// Similar to get and set accessors for properties, subscribe and unsubscribe to events via + -.
public event EventHandler MyEvent
{
    add { m_MyEvent += value; } //
    remove { m_MyEvent -= value; } //
}
  • The "EventHandler" that defines the event is a delegate, it can be any delegate type, most of the built-in generic delegates are used in C#.EventHandler<TEventArgs>
  • The compilation generates a private delegate fieldm_MyEvent, which is the heart of the matter.
  • generatedaddSubscribe,removeThe unsubscribe method, which controls the addition and removal of delegates, is used with the+=-=Syntax. The above code is simplified, the actual code to be a little more complex, mainly to add a thread-safe processing.
  • Custom events can also be created directly using the above example'saddremoveThe way it is encapsulated.

📢 From the above you can see that events are encapsulated based on delegates, similar to properties encapsulating fields. The external can onlyaddSubscribe,removeUnsubscribe and the execution (triggering) of events (delegates) can only be done internally.

3.3. Standard event model

There are a large number of event applications within C# that form a default event (standard) schema that mainly defines the delegates used to create events, event parameters.

  • : event parameter, which is the core of the standard event model and serves as a base class for event parameters to inherit from custom implementations of some of the fields (attributes) to be passed by the event.
  • The delegate return value isvoid
  • Delegate two parameterssenderEventArgssenderis the object that triggers the event and is also the broadcaster of the event;EventArgsis the event's parameter.
  • The delegate ends with the name "EventHandler".
  • Built-in generalized versionsEventHandler<TEventArgs> can fulfill the above conditions and is a more general standard event delegate.
public class EventArgs
{
	public static readonly EventArgs Empty = new EventArgs();
}
public delegate void EventHandler(object? sender, EventArgs e);
//Generic generic version
public delegate void EventHandler<TEventArgs>(object? sender, TEventArgs e);

Of course this this pattern is not required, just a programming habit or norm.

3.4. Should I use delegates or events?

Event is based on delegate, the function of the event delegate mostly can support, both function and use are relatively similar, both support unicast, multicast, late binding, then how to choose between the two?

  • Events generally have no return value, but of course you can if you want to.
  • Events provide better encapsulation, similar to the encapsulation of fields by properties, in line with the principle of opening and closing. Events can only be executed internally, externally only+=Subscribe,-=Unsubscribe.

So the conclusion

  • Simple scenarios with delegates: one-to-one communication, passing methods.
  • Complex scenarios with events: one-to-many communication, need for security permission encapsulation.

04. other - delegated performance issues?

From the previous section we know that all delegates are actually a multicast delegate type, and that the execution of a delegate is actually the execution of theInvoke()method, which internally iterates through the list of methods to execute, which is quite a bit slower than a direct method call.

public static int Sum(int x, int y) => x + y; //methodologies
public static Func<int, int, int> SumFunc = Sum;//commission

public void Sum_MethodCall() //直接调用methodologies
{
	int sum = 0;
	for (int i = 0; i < 10; i++)
	{
		sum += Sum(i, i + 1);
	}
}
public void Sum_FuncCall() //调用commission
{
	int sum = 0;
	for (int i = 0; i < 10; i++)
	{
		sum += SumFunc(i, i + 1);
	}
}

exist.Net6run inBenchmarkThe test comparison is as follows, the direct call is 4-5 times more efficient.

exist.Net7.Net8A number of performance optimizations have been made to achieve similar performance for delegate calls as for direct calls, so you no longer have to worry about performance flaws with delegates. The following figure shows the performance of the.Net8centerBenchmarkTesting.


bibliography

  • and the delegate keyword
  • Standard .NET Event Patterns
  • Still can't figure out [commissions and events]?, suitable for starters.
  • Understanding events in C# from the ground upIt's a little more detailed and good for getting started.
  • What's the deal with delegates and events in C#?B Station Video
  • The evolution of delegate performance in .NET