C#:为什么在多线程环境中操作委托是线程安全的? c的函数指针=>C#委托进化过程详解

文章目录



函数指针=>委托

c 复制代码
typedef int (*Cal)(int, int);  //定义函数指针
int Sum(int a, int b)
{
	return a + b;
}
int main()
{
	int a = 9, b = 2, c = 0;
	c = Sum(a, b);
	printf("%d\n", c);

	Cal cal = ∑  //创建Sum的函数指针变量cal
	c = cal(a, b);
	printf("%d\n", c);
	return 0;
}

//都输出11

Sum(a, b):直接调用==>CPU通过函数名直接获得函数地址并开始执行.
cal(a, b):间接调用==>通过函数指针来调用函数,CPU通过函数指针指向的值找到函数的地址并开始执行.
上面两者的实现效果一样.

那么问题来了,大家不觉得上面的函数指针实现语法*typedef int (Cal)(int, int); 太复杂了?感觉功能也比较单一,用起来太麻烦.所以就有了C#的委托出现

csharp 复制代码
 	delegate int Cal(int a,int b);
    class Program
    {
        static void Main(string[] args)
        {
            int a = 9, b = 2;
            Console.WriteLine(Sum(a,b));

            Cal cal = new Cal(Sum);
            Console.WriteLine(cal(a,b));
        }
        static int Sum(int a,int b)
        { return a + b; }
    }

C#中委托的声明和像函数的声明,只需要在前面加delegate关键字.
用上面的代码就可以实现C语言函数指针同样的效果.但使用起来更加方便.且委托的用处远不止于此.

使用委托

1.委托和类一样.但类是数据和方法的集合,委托只是方法集合,所以委托的声明应该放在命名空间中,与类同级别.

csharp 复制代码
namespace shh
{
    delegate int Cal(int a, int b);

    class Calculator
    {
        public int Sum(int a, int b)
        { return a + b; }
        public int Sub(int a, int b)
        { return a - b; }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Calculator calculator = new Calculator();
            Cal cal = new Cal(calculator.Sum);
            Console.WriteLine(cal(9,2));
        }
    }
}

2.委托以delegate关键字开头,跟想用使用的方法有相同的参数和返回类型.(参数名不需要相同).

csharp 复制代码
delegate int Cal(int a, int b);
public int Sum(int a, int b)

3.创建委托类型变量和创建委托对象不同

csharp 复制代码
 Cal cal ;//创建委托类型变量,没有在堆上开空间
 cal = new Cal(calculator.Sum);//创建委托对象,在堆上开空间

4.创建委托对象可以使用实例方法和静态方法

csharp 复制代码
namespace shh
{
    delegate int Cal(int a, int b);

    class Calculator
    {
        public int Sum(int a, int b)
        { return a + b; }
        public static int Sub(int a, int b)
        { return a - b; }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Calculator calculator = new Calculator();
            Cal cal1 = new Cal(calculator.Sum);
            Cal cal2 = new Cal(Calculator.Sub);
        }
    }

}

这里需要注意的是静态方法得用类名.方法(Calculator.Sub)

5.快捷语法

csharp 复制代码
Cal cal1 = new Cal(calculator.Sum);
Cal cal2 = calculator.Su

两条语句等价,系统会自动识别并进行类型转换.
6.给委托赋值
改变委托变量的引用.赋值会创建新的委托,旧的委托会被回收

csharp 复制代码
cal1 = Calculator.Sub;

创建新的委托除了为委托分配内存,还会把第一个方法(Sub)添加到委托的调用列表里面.

组合委托

csharp 复制代码
namespace shh
{
    delegate void Cal(int a, int b);

    class Calculator
    {
        public void  Sum(int a, int b)
        { Console.WriteLine(a+b); }
        public void Sub(int a, int b)
        { Console.WriteLine(a-b); }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Calculator calculator = new Calculator();
            Cal cal1 = new Cal(calculator.Sum);
            cal1 += calculator.Sub;
            cal1(9, 2);
        }
    }
}

委托变量+=方法,会把方法添加到调用列表里面.

如果委托变量-=方法,会把方法踢出到调用列表,如果有多份(踢掉列表最后的那份)

组合委托在调用时,会执行多个方法(委托的调用列表里面的方法).

为什么在多线程环境中操作委托是线程安全的

这个运算会创建一个新的委托,旧的委托会被垃圾回收器回收.这也是为什么在多线程环境中,操作委托是线程安全的,因为每次修改都会创建一个新的对象,不会影响其他线程对旧对象的使用。

调用带有返回值的委托

csharp 复制代码
namespace shh
{
    delegate int Cal(int a, int b);

    class Calculator
    {
        public int Sum(int a, int b)
        { return a + b; }
        public int Sub(int a, int b)
        { return a - b; }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Calculator calculator = new Calculator();
            Cal cal1 = new Cal(calculator.Sum);
            cal1 += calculator.Sub;
            Console.WriteLine(cal1(9,2));
        }
    }

}

只会返回最后一个方法返回的值,其他值都会被忽略.

调用带引用的委托

这个也很好理解,因为我们调用委托时是按照他参数列表的顺序进行的,使用引用的值会被不断地加.

csharp 复制代码
    delegate int MyDel(ref int a);

    class Test
    {
        public int Add1(ref int a)
        { return a += 5; }
        public int Add2(ref int a)
        { return a += 10; }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Test test = new Test();
            MyDel mydel = test.Add1;
            mydel += test.Add2;
            int a = 0;
            Console.WriteLine(mydel(ref a));
        }
    }

总结

委托其实很好理解,使用也很简单.就是一个函数指针的进化体,不要被他的名字吓住了.无非就是功能和使用体验上会优于函数指针.

相关推荐
三无少女指南34 分钟前
深入理解JVM的安全点与安全区域
jvm·安全
wanhengidc1 小时前
云手机和虚拟机的区别都有哪些?
运维·服务器·安全·智能手机·玩游戏
.格子衫.1 小时前
022数据结构之树状数组——算法备赛
数据结构·算法·1024程序员节
黑科技Python1 小时前
生活中的“小智慧”——认识算法
学习·算法·生活
Yupureki1 小时前
从零开始的C++学习生活 16:C++11新特性全解析
c语言·数据结构·c++·学习·visual studio
ScilogyHunter2 小时前
C语言标准库完全指南
c语言·开发语言
sali-tec2 小时前
C# 基于halcon的视觉工作流-章52-生成标定板
开发语言·图像处理·人工智能·算法·计算机视觉
xixixi777772 小时前
信息安全和网络安全的区分在哪
网络·安全·web安全·信息安全
IT古董2 小时前
【第五章:计算机视觉-项目实战之推荐/广告系统】2.粗排算法-(4)粗排算法模型多目标算法(Multi Task Learning)及目标融合
人工智能·算法·1024程序员节
熬了夜的程序员2 小时前
【LeetCode】89. 格雷编码
算法·leetcode·链表·职场和发展·矩阵