C#学习(一)委托的概念和使用
前言 C#学习系列是根据我以前的笔记整理出来复习,顺便发一下文章做个记录。 先引用一个介绍: C# 中的委托(Delegate)类似于 C 或 C++ 中函数的指针。委托(Delegate) 是存有对某个方法的引用的一种引用类型变量。引用可在运行时被改变。 委托(Delegate)特别用于实现事件和回调方法。所有的委托(Delegate)都派生自 System.Delegate 类。 根据使用其他
目录7 个章节
前言
C#学习系列是根据我以前的笔记整理出来复习,顺便发一下文章做个记录。
先引用一个介绍:
C# 中的委托(Delegate)类似于 C 或 C++ 中函数的指针。委托(Delegate) 是存有对某个方法的引用的一种引用类型变量。引用可在运行时被改变。 委托(Delegate)特别用于实现事件和回调方法。所有的委托(Delegate)都派生自 System.Delegate 类。
根据使用其他语言的经验,在Python和js中,函数(方法)也是一种类型,可以赋值给变量,然后通过变量来调用。C#里也差不多是这个道理,不过C#的委托可以存入多个函数(方法),当调用这个委托的时候同时调用多个方法,这个叫做多播委托。委托具体的实现细节我还没了解,等接下来的学习中继续深入哈~
代码例子
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;
}
}
}
一点理解
要理解委托,得先理解C#的类型系统,CLR支持两种基本类型,值类型和引用类型。下面这张图就可以比较清楚的看到C#的类型系统,大部分同学都是只学过Java的,可能没有深入学过C语言或C++,C#之所以能在各种场景下保持高性能(比如游戏引擎这种计算密集场景),很大程度就是优秀的类型系统设计。

值类型和引用类型介绍
值类型(Value Type),值类型实例通常分配在线程的堆栈(stack)上,并且不包含任何指向实例数据的指针,因为变量本身就包含了其实例数据。其在 MSDN 的定义为值类型直接包含它们的数据,值类型的实例要么在堆栈上,要么内联在结构中。我们由上图可知,值类型主要包括简单类型、结构体类型和枚举类型等。通常声明为以下类型:int、char、float、long、bool、double、struct、enum、short、byte、decimal、sbyte、uint、ulong、ushort 等时,该变量即为值类型。
引用类型(Reference Type),引用类型实例分配在托管堆(managed heap)上,变量保存了实例数据的内存引用。其在 MSDN 中的定义为引用类型存储对值的内存地址的引用,位于堆上。我们由上图可知,引用类型可以是自描述类型、指针类型或接口类型。而自描述类型进一步细分成数组和类类型。类类型是则可以是用户定义的类、装箱的值类型和委托。通常声明为以下类型:class、interface、delegate、object、string 以及其他的自定义引用类型时,该变量即为引用类型。
内存分配
数据在内存中的分配位置,取决于该变量的数据类型。由上可知,值类型通常分配在线程的堆栈上,而引用类型通常分配在托管堆上,由 GC 来控制其回收。如下图:

我们知道,每个变量或者程序都有其堆栈,不同的变量不能共有同一个堆栈地址,因此 myStruct 和myStruct2 在堆栈中一定占用了不同的堆栈地址,尽管经过了变量的传递,实际的内存还是分配在不同的地址上,如果我们再对 myStruct2 变量改变时,显然不会影响到 myStruct 的数据。从图中我们还可以显而易见的看出,myStruct 在堆栈中包含其实例数据,而 myClass 在堆栈中只是保存了其实例数据的引用地址,实际的数据保存在托管堆中。因此,就有可能不同的变量保存了同一地址的数据引用,当数据从一个引用类型变量传递到另一个相同类型的引用类型变量时,传递的是其引用地址而不是实际的数据,因此一个变量的改变会影响另一个变量的值。从上面的分析就可以明白的知道这样一个简单的道理:值类型和引用类型在内存中的分配区别是决定其应用不同的根本原因,由此我们就可以很容易的解释为什么参数传递时,按值传递不会改变形参值,而按址传递会改变行参的值,道理正在于此。
对于内存分配的更详细位置,可以描述如下:
-
值类型变量做为局部变量时,该实例将被创建在堆栈上;而如果值类型变量作为类型的成员变量时,它将作为类型实例数据的一部分,同该类型的其他字段都保存在托管堆上,这点我们将在接下来的嵌套结构部分来详细说明。
-
引用类型变量数据保存在托管堆上,但是根据实例的大小有所区别,如下:如果实例的大小小于 85000Byte 时,则该实例将创建在 GC 堆上;而当实例大小大于等于 85000byte 时,则该实例创建在 LOH(Large Object Heap)堆上。
写在后面
OK,大概就写到这里,明天继续学习~
欢迎交流
交流问题请在微信公众号后台留言,每一条信息我都会回复哈~
_EOF_
C#学习(一)委托的概念和使用
- 本文作者:
- 程序设计实验室
- 版权声明:
- 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
相关文章
花了不少时间,修复了一个SharpIco生成图标的bug
SharpIco bug修好了?真把自己当回事了,高分辨率图片转ico就崩,这逻辑够清奇。原来是位深度闹的,24转32而已,用点真本事解决?
2025-07-01 · C# · 约 1 分钟
一次小而美的重构:使用 C# 在 Avalonia 中生成真正好看的词云
重构StarBlogPublisher中词云展示功能的过程。作者对比了两个流行的C#词云库,最终选择了功能更强大的Sdcb.WordCloud。文章详细说明了如何使用该库获取词频数据并生成词云图像,并展示了重构前后的效果对比。
2025-04-26 · C# · 约 1 分钟
让 C# 也拥有 gofmt 般的体验:用 CSharpier + Roslyn 实现 csfmt
C# 代码长到像答辩?用 CSharpier 自动折行,再用 Roslyn 修正 K&R 花括号,Taskfile 一键收尾。再补上 format-check,省得 CI 反复制造 Git diff。两步流水线并非原生 gofmt,顺序和版本仍得老实锁住。
2026-10-22 · C# · 约 1 分钟
C#学习(二)匿名方法和委托的多种使用方式
前言 上次简单介绍了委托的概念和使用,今天继续,并且引入匿名方法的概念,匿名方法在很多语言里面都有,比如Python、js、java8,尤其js,前端小伙伴可能深受js回调地狱之苦,这里面匿名方法功不可没。 js的匿名方法就像这样: 这还没用lambda,因为我想下篇再介绍lambda。 document.querySelector('.btn').onclick = function () {
2021-12-18 · C# · 约 1 分钟
讨论
共 0 条加载评论中…