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 许可协议。转载请注明出处!
相关文章
一次小而美的重构:使用 C# 在 Avalonia 中生成真正好看的词云
重构StarBlogPublisher中词云展示功能的过程。作者对比了两个流行的C#词云库,最终选择了功能更强大的Sdcb.WordCloud。文章详细说明了如何使用该库获取词频数据并生成词云图像,并展示了重构前后的效果对比。
2025-04-26 · C# · 约 1 分钟
Spark.NET:一个试图把 Django / Rails 式开发体验带回 .NET 世界的全栈 Web 框架。
又一个把Django/Rails那套“约定优于配置”搬进.NET生态的尝试,看着有点意思。预置了认证、队列、定时任务这些破事,省得自己天天重复造轮子。但767个star连个正式release都没有,就敢打着“生产级”旗号?呵,C#全栈爱好者们可以先玩玩,但别指望它能替代ASP.NET Core的体量。
2026-04-29 · C# · 约 1 分钟
在.NetCore实现图片缩放与裁剪-基于ImageSharp
前言 ~~(突然发现断更有段时间了~~ 最近在做博客的时候,需要实现一个类似Lorempixel、LoremPicsum这样的随机图片功能,图片有了,还需要一个根据输入的宽度高度获取图片的功能,由于之前处理图片时使用到了ImageSharp库,所以这次我立刻就想到用它。 分析需求 图片库中的图片基本都是我之前收集的壁纸什么的,尺寸参差不齐,有横屏的也有竖屏 然后包装成接口只需要输入宽度和高度,就能
2022-06-17 · C# · 约 1 分钟
开发现代化的.NetCore控制台程序:(4)使用GithubAction自动构建以及发布nuget包
GitHub Action自动构建发布nuget包,省时省力。Secrets管理token,workflow一劳永逸。吐槽nuget推送重复输入token,烦人!
2023-11-13 · C# · 约 1 分钟
讨论
共 0 条加载评论中…