C# Learning (1): The Concept and Use of Delegates
Preface The C# learning series is compiled from my previous notes for review, and I'm posting articles along the way as a record. First, here's a quoted introduction: Delegates in C# are similar to function pointers in C or C++. A delegate is a reference type variable that holds a reference to a method. The reference can be changed at runtime. Delegates are especially used to implement events and callback methods. All delegates derive from the System.Delegate class. Based on using other
目录7 个章节
Preface
The C# learning series is compiled from my previous notes for review, and I'm posting articles along the way as a record.
First, here's a quoted introduction:
Delegates in C# are similar to function pointers in C or C++. A delegate is a reference type variable that holds a reference to a method. The reference can be changed at runtime. Delegates are especially used to implement events and callback methods. All delegates derive from the System.Delegate class.
Based on experience with other languages, in Python and JS, functions (methods) are also a type: they can be assigned to variables and then invoked through those variables. It's pretty much the same idea in C#, except that a C# delegate can hold multiple functions (methods), and when the delegate is invoked, multiple methods are called at the same time—this is called a multicast delegate. I haven't looked into the specific implementation details of delegates yet; I'll go deeper in the upcoming studies~
Code Example
using System;
namespace DelegateAndEvent
{
class Program
{
delegate void voidDelegate();
delegate int intDelegate();
static void Main(string[] args)
{
// 定义一个多播委托
voidDelegate d1 = T1;
d1 += T2;
d1 += T3;
d1 += T4;
// 执行委托
// 会同时执行t1,t2,t3,t4四个方法
d1();
}
static void T1()
{
Console.WriteLine("1");
}
static void T2()
{
Console.WriteLine("2");
}
static void T3()
{
Console.WriteLine("3");
}
static void T4()
{
Console.WriteLine("4");
}
static int Q1()
{
return 1;
}
static int Q2()
{
return 2;
}
static int Q3()
{
return 3;
}
}
}
A Bit of Understanding
To understand delegates, you first need to understand C#'s type system. The CLR supports two fundamental types: value types and reference types. The diagram below gives a fairly clear picture of C#'s type system. Most of you have only studied Java and may not have gone deep into C or C++. The reason C# can maintain high performance in various scenarios (such as computationally intensive ones like game engines) is largely due to its excellent type system design.

Introduction to Value Types and Reference Types
Value types (Value Type): instances of value types are typically allocated on the thread's stack, and they do not contain any pointer to the instance data, because the variable itself contains its instance data. MSDN defines them as: value types directly contain their data; instances of value types are either on the stack or inlined in a structure. As we can see from the diagram above, value types mainly include simple types, struct types, enum types, and so on. When a variable is declared as one of the following types: int, char, float, long, bool, double, struct, enum, short, byte, decimal, sbyte, uint, ulong, ushort, etc., that variable is a value type.
Reference types (Reference Type): instances of reference types are allocated on the managed heap, and the variable holds a memory reference to the instance data. MSDN defines them as: reference types store a reference to the memory address of a value, located on the heap. As we can see from the diagram above, a reference type can be a self-describing type, a pointer type, or an interface type. Self-describing types are further subdivided into arrays and class types. Class types can be user-defined classes, boxed value types, and delegates. When a variable is declared as one of the following types: class, interface, delegate, object, string, or other custom reference types, that variable is a reference type.
Memory Allocation
Where data is allocated in memory depends on the variable's data type. As described above, value types are typically allocated on the thread's stack, while reference types are typically allocated on the managed heap, with their reclamation controlled by the GC. As shown below:

We know that every variable or program has its stack, and different variables cannot share the same stack address. Therefore myStruct and myStruct2 must occupy different stack addresses; even after variable assignment, the actual memory is still allocated at different addresses. If we then change the myStruct2 variable, it clearly will not affect myStruct's data. From the diagram we can also readily see that myStruct contains its instance data on the stack, whereas myClass on the stack only holds a reference address to its instance data, with the actual data stored on the managed heap. Therefore, it is possible for different variables to hold a data reference to the same address. When data is passed from one reference-type variable to another reference-type variable of the same type, what is passed is the reference address rather than the actual data, so a change to one variable will affect the value of the other. From the analysis above, we can clearly understand this simple principle: the difference in how value types and reference types are allocated in memory is the fundamental reason their applications differ. From this we can easily explain why, when passing parameters, passing by value does not change the formal parameter's value, whereas passing by address does change the formal parameter's value—this is exactly why.
A more detailed description of where memory is allocated is as follows:
-
When a value-type variable is used as a local variable, the instance is created on the stack; if a value-type variable is a member variable of a type, it becomes part of that type's instance data and is stored on the managed heap together with the type's other fields. We will explain this in detail in the upcoming nested-structure section.
-
Reference-type variable data is stored on the managed heap, but there is a distinction based on instance size, as follows: if the instance size is less than 85000 bytes, the instance is created on the GC heap; when the instance size is greater than or equal to 85000 bytes, the instance is created on the LOH (Large Object Heap).
Afterword
OK, that's about it for now. I'll continue studying tomorrow~
Welcome to Discuss
For questions, please leave a message on the WeChat official account backend. I will reply to every message~
- WeChat official account: 画星星高手
- Coding livestream: https://live.bilibili.com/11883038
- Zhihu: https://www.zhihu.com/people/dealiaxy
- Jianshu: https://www.jianshu.com/u/965b95853b9f
_EOF_
C# Learning (1): The Concept and Use of Delegates
- 本文作者:
- 程序设计实验室
- 版权声明:
- 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
相关文章
C#高性能数组拷贝实验
前言 昨天 wc(Wyu_Cnk) 提了个问题 C# 里多维数组拷贝有没有什么比较优雅的写法? 这不是问对人了吗?正好我最近在搞图像处理,要和内存打交道,我一下就想到了在C#里面直接像C/C++一样做内存拷贝。 优雅?no,要的就是装逼,而且性能还要强🕶 概念 首先澄清一下 C# 里的多维数组 (Multi-dimensional Array) 是这样的 byte[,] arr = new by
2023-01-30 · C# · 约 1 分钟
纯 C#实现+AOT 打造的智能PDF目录提取工具 PdfTocExtractor
用C#写个PDF目录提取工具?挺有意思,还整什么语义识别,效果真有那么好吗?AOT编译挺香,但别搞得太复杂就行。
2025-08-20 · C# · 约 1 分钟
纯 C#实现+AOT 打造的多功能图片处理工具 ImageGlider
纯C#+AOT打造的ImageGlider,图像处理功能堆得像山,还非得标榜零依赖?ImageSharp封装得倒是挺顺溜,就是不知道这模块化架构能走多远。Web API和CLI工具有点东西,但别指望我夸太离谱。
2025-08-05 · C# · 约 1 分钟
分析C#项目的单元测试覆盖率,提高代码质量
C#覆盖率分析?不就是用工具生成报告吗?VSCode、VS、Rider哪个不能干?真当这是啥高科技?
2025-08-06 · C# · 约 1 分钟
讨论
共 0 条加载评论中…