C#学习

编程入门 行业动态 更新时间:2024-10-26 17:33:02

C#学习

C#学习

委托

委托(delegate)是函数指针的升级版
委托是一种类(Class),类是数据类型,所以委托也是一种数据类型

static void Main(string[] args)
{Type t = typeof(Action);Console.WriteLine(t.IsClass);//输出True,则委托为类(Class)
}

直接调用与间接调用

  • 直接调用:使用函数名来调用函数,CPU通过函数名直接获得函数所在地址并开始执行
  • 间接调用:使用函数指针来调用函数,CPU通过读取函数指针存储的值获得函数所在地址并开始执行
    直接调用和间接调用效果一样
    C语言非函数指针直接调用
 #include <stdio.h>
int Add(int x, int y)//加法函数
{return x + y;
}
int Sub(int x, int y)//减法函数
{return x - y;
}
int main()
{int x = 100;int y = 200;int z;z = Add(x, y);//输入函数名直接调用printf("%d + %d = %d\n", x, y, z);z = Sub(x, y);printf("%d - %d = %d\n", x, y, z);return 0;
}

C语言使用函数指针间接调用

#include <stdio.h>
typedef int(*Cal)(int x, int y);//获得函数指针类型
int Add(int x, int y)
{return x + y;
}
int Sub(int x, int y)
{return x - y;
}
int main()
{int x = 100;int y = 200;int z;Cal funcPoint1 = &Add;//声明函数指针类型的变量Cal funcPoint2 = &Sub;z = funcPoint1(x, y);printf("%d + %d = %d\n", x, y, z);z = funcPoint2(x, y);printf("%d - %d = %d\n", x, y, z);return 0;
}

一切皆为地址

  • 变量(数据)是以某个地址为起点的一段内存中所存储的值
  • 函数(算法)是以某个地址为起点的一段内存中所存储的一组机器语言指令

Action委托例子

Action委托只能对返回值为空的方法进行委托

internal class Program
{static void Main(string[] args){Calculator calculator = new Calculator();Action action = new Action(calculator.Report);//Action(void() target)//Action创建实例,后面圆括号内只能接一个返回值为空的方法calculator.Report();//直接调用action.Invoke();//间接调用action();//简写}
}
class Calculator
{public void Report(){Console.WriteLine("I'm Calculator.");}public int Add(int a, int b){return a + b;}public int Sub(int a, in int b) {return a - b;}
}

Function委托例子

Function委托有17种类型重载
代码中写为:Func

internal class Program
{static void Main(string[] args){Calculator calculator = new Calculator();Func<int, int, int> func1 = new Func<int, int, int>(calculator.Add);Func<int, int, int> func2 = new Func<int, int, int>(calculator.Sub);int x = 100;int y = 200;int z = 0;z = func1.Invoke(x, y);//或者写为:z = func1(x, y);Console.WriteLine(z);z = func2.Invoke(x, y);//或者写为:z = func2(x, y);Console.WriteLine(z);}
}
class Calculator
{public void Report(){Console.WriteLine("I'm Calculator.");}public int Add(int a, int b){return a + b;}public int Sub(int a, int b) {return a - b;}
}

C#自带的委托

即为 Action 和 Function 这两种委托,可以直接用

委托的声明

即为自定义委托
虽然委托是类,但委托声明方式与一般的类不同,声明更像是C/C++中的函数指针的声明,主要是为了增加可读性和继承C/C++的传统

public delegate double Cal(double x, double y);
//第一个double - 目标方法的返回类型
//圆括号内 - 目标方法的参数列表
internal class Program
{static void Main(string[] args){Calculator calculator = new Calculator();Cal cal1 = new Cal(calculator.Add);Cal cal2 = new Cal(calculator.Sub);Cal cal3 = new Cal(calculator.Mul);Cal cal4 = new Cal(calculator.Div);double x = 100;double y = 200;double z;z = cal1.Invoke(x, y);//".Invoke"可以删去Console.WriteLine(z);z = cal2.Invoke(x, y);Console.WriteLine(z);z = cal3.Invoke(x, y);Console.WriteLine(z);z = cal4.Invoke(x, y);Console.WriteLine(z);}
}
class Calculator
{public double Add(double x, double y){return x + y;}public double Sub(double x, double y){return x - y;}public double Mul(double x, double y){return x * y;}public double Div(double x, double y){return x / y;}
}

注意:委托与所封装的方法必须“类型兼容

delegate double Cal(double x, double y);
------------double Add(double x, double y) { return x + y; }
------------double Sub(double x, double y) { return x - y; }
------------double Mul(double x, double y) { return x * y; }
------------double Div(double x, double y) { return x / y; }

声明委托时不要放错位置,要声明在名称空间体中,以免被C#编译器认为是嵌套类(在类中声明一个类)

委托的一般使用

委托一般当作方法的参数传到另一个方法中去

  • 模板方法:借用指定的外部方法来产生结果
    • 可以增加代码重复使用率
internal class Program
{static void Main(string[] args){ProjectFactory projectFactory = new ProjectFactory();WrapFactory wrapFactory = new WrapFactory();Func<Project> func1 = new Func<Project>(projectFactory.MakeGame);Func<Project> func2 = new Func<Project>(projectFactory.MakeMovie);Box box1 = wrapFactory.WrapProject(func1);Box box2 = wrapFactory.WrapProject(func2);Console.WriteLine(box1.Project.Name);Console.WriteLine(box2.Project.Name);}
}
class Project//项目
{public string Name { get; set; }
}
class Box//项目封装箱
{public Project Project {  get; set; }
}
class WrapFactory//封装工厂
{public Box WrapProject(Func<Project> getProject){   //模板方法Box box = new Box();//创建一个封装箱Project project = getProject.Invoke();//获取一个项目box.Project = project;//将项目放进封装箱中return box;//返还封装箱}
}
class ProjectFactory//项目工厂
{public Project MakeGame(){Project project = new Project();project.Name = "Game";return project;}public Project MakeMovie(){Project project = new Project();project.Name = "Movie";return project;}
}
  • 回调(Callback)方法:调用指定的外部方法。一般位于主调方法的末尾,且一般没有返回值
//项目价格高于100时,打印生产项目时的标准时间
internal class Program
{static void Main(string[] args){ProjectFactory projectFactory = new ProjectFactory();WrapFactory wrapFactory = new WrapFactory();Func<Project> func1 = new Func<Project>(projectFactory.MakeGame);Func<Project> func2 = new Func<Project>(projectFactory.MakeMovie);Logger logger = new Logger();Action<Project> log = new Action<Project>(logger.Log);Box box1 = wrapFactory.WrapProject(func1, log);Box box2 = wrapFactory.WrapProject(func2, log);Console.WriteLine(box1.Project.Name);Console.WriteLine(box2.Project.Name);}
}
class Logger//记录程序运行状态
{public void Log(Project project){Console.WriteLine("Project \"{0}\" created at {1}, Price is {2}", project.Name, DateTime.UtcNow, project.Price);//DateTime.UtcNow - 不带时区的时间}
}
class Project//项目
{public string Name { get; set; }public double Price { get; set; }//项目价格
}
class Box//项目封装箱
{public Project Project {  get; set; }
}
class WrapFactory//封装工厂
{public Box WrapProject(Func<Project> getProject, Action<Project> logCallback){	//开始回调Box box = new Box();//创建一个封装箱Project project = getProject.Invoke();//获取一个项目if (project.Price > 100){logCallback(project);}box.Project = project;//将项目放进封装箱中return box;}
}
class ProjectFactory//项目工厂
{public Project MakeGame(){Project project = new Project();project.Name = "Game";project.Price = 298;return project;}public Project MakeMovie(){Project project = new Project();project.Name = "Movie";project.Price = 50;return project;}
}

注意:委托难精通、易使用、功能强大

  • 这是一种方法级别的紧耦合
  • 容易使可读性下降,增加debug难度
  • 把委托回调、异步调用、多线程纠缠在一起时,会让代码变得很难维护
  • 委托使用不当可能造成内存泄漏和程序性能下降

委托的高级使用

多播(Multicast)委托

即一个委托内封装着多个委托

  • 单播委托
    • 一个委托封装一个方法
//using System.Threading;
internal class Program
{static void Main(string[] args){Student s1 = new Student() { ID = 1, PenColor = ConsoleColor.Red };Student s2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };Student s3 = new Student() { ID = 3, PenColor = ConsoleColor.Blue };Action a1 = new Action(s1.DoHomework);Action a2 = new Action(s2.DoHomework);Action a3 = new Action(s3.DoHomework);a1.Invoke();a2.Invoke();a3.Invoke();}
}
class Student
{public int ID { get; set; }//学生IDpublic ConsoleColor PenColor { get; set; }//学生使用的笔的颜色public void DoHomework(){for (int i = 0; i < 5; i++){Console.ForegroundColor = this.PenColor;if (i == 0){Console.WriteLine("Student {0} starts doing homework.", this.ID);continue;}Thread.Sleep(1000);//1000ms = 1s//在哪个线程中调用了"Thread.Sleep",此线程就暂停 1sConsole.WriteLine("Student {0} has done homework {1} hour(s).", this.ID, i);}}
}
  • 多播委托
//将单播委托例子中的Main方法修改为如下
Student s1 = new Student() { ID = 1, PenColor = ConsoleColor.Red };
Student s2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
Student s3 = new Student() { ID = 3, PenColor = ConsoleColor.Blue };
Action a1 = new Action(s1.DoHomework);
Action a2 = new Action(s2.DoHomework);
Action a3 = new Action(s3.DoHomework);a1 += a2;//将a2封装进a1
a1 += a3;//将a3封装进a1
a1.Invoke();//执行顺序为封装的先后顺序

异步调用

同步调用:一个人做完后,下一个人在前一个人做完的基础上接着做
异步调用:两个人同时做
同步调用与异步调用:每一个运行的程序是一个进程(Process),一个进程可以有一个或者多个线程(Thread)。同步调用是在一个线程内进行,异步调用是在多个线程中进行
同步调用是在单线程中进行串行调用
异步调用是在多线程中进行并行调用
可以利用委托进行隐式异步调用

  • 同步调用
//修改上段代码中的Main方法
Student s1 = new Student() { ID = 1, PenColor = ConsoleColor.Red };
Student s2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
Student s3 = new Student() { ID = 3, PenColor = ConsoleColor.Blue };
s1.DoHomework();
s2.DoHomework();
s3.DoHomework();
//一个一个调用
//使用委托进行间接同步调用
Student s1 = new Student() { ID = 1, PenColor = ConsoleColor.Red };
Student s2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
Student s3 = new Student() { ID = 3, PenColor = ConsoleColor.Blue };
Action a1 = new Action(s1.DoHomework);
Action a2 = new Action(s2.DoHomework);
Action a3 = new Action(s3.DoHomework);a1.Invoke();
a2.Invoke();
a3.Invoke();
//单播、多播委托都能同步调用
//a1 += a2;
//a1 += a3;
//a1.Invoke();for (int i = 0; i < 10; i++)
{Console.ForegroundColor = ConsoleColor.Cyan;Thread.Sleep(1000);Console.WriteLine("Main Thread {0}.", i);
}
  • 隐式异步调用
    • 分支线程是由BeginInvoke自动生成的
//修改上面的Main方法
//使用委托进行隐式异步调用
Student s1 = new Student() { ID = 1, PenColor = ConsoleColor.Red };
Student s2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
Student s3 = new Student() { ID = 3, PenColor = ConsoleColor.Blue };Action a1 = new Action(s1.DoHomework);
Action a2 = new Action(s2.DoHomework);
Action a3 = new Action(s3.DoHomework);a1.BeginInvoke(null, null);//BeginInvoke会自动生成一个分支线程,再在分支线程中调用方法
//需要两个参数
//1. 异步调用的回调 - 调用完方法后后续的行动
//2. 向回调函数中传递参数
//不使用参数则写做 null
a2.BeginInvoke(null, null);
a3.BeginInvoke(null, null);
for (int i = 0; i < 10; i++)
{Console.ForegroundColor = ConsoleColor.Cyan;Thread.Sleep(1000);Console.WriteLine("Main Thread {0}.", i);
}
  • 显示异步调用
    • 手动生成线程
//使用Thread
Student s1 = new Student() { ID = 1, PenColor = ConsoleColor.Red };
Student s2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
Student s3 = new Student() { ID = 3, PenColor = ConsoleColor.Blue };Thread t1 = new Thread(new ThreadStart(s1.DoHomework));
Thread t2 = new Thread(new ThreadStart(s2.DoHomework));
Thread t3 = new Thread(new ThreadStart(s3.DoHomework));t1.Start();
t2.Start();
t3.Start();for (int i = 0; i < 10; i++)
{Console.ForegroundColor = ConsoleColor.Cyan;Thread.Sleep(1000);Console.WriteLine("Main Thread {0}.", i);
}
//使用Task
Student s1 = new Student() { ID = 1, PenColor = ConsoleColor.Red };
Student s2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
Student s3 = new Student() { ID = 3, PenColor = ConsoleColor.Blue };Task t1 = new Task(new Action(s1.DoHomework));
Task t2 = new Task(new Action(s2.DoHomework));
Task t3 = new Task(new Action(s3.DoHomework));t1.Start();
t2.Start();
t3.Start();for (int i = 0; i < 10; i++)
{Console.ForegroundColor = ConsoleColor.Cyan;Thread.Sleep(1000);Console.WriteLine("Main Thread {0}.", i);
}

使用接口取代委托

应适当使用接口(interface)取代一些对委托的使用,可以避免一些不必要的麻烦还可以有相同的效果

internal class Program
{static void Main(string[] args){IProjectFactory gameFactory = new GameFactory();IProjectFactory movieFactory = new MovieFactory();WrapFactory wrapFactory = new WrapFactory();Box box1 = wrapFactory.WrapProject(gameFactory);Box box2 = wrapFactory.WrapProject(movieFactory);Console.WriteLine(box1.Project.Name);Console.WriteLine(box2.Project.Name);}
}
interface IProjectFactory
{	//接口Project Make();
}
class GameFactory : IProjectFactory
{public Project Make(){Project project = new Project();project.Name = "Game";return project;}
}
class MovieFactory : IProjectFactory
{Project IProjectFactory.Make(){Project project = new Project();project.Name = "Movie";return project;}
}class Project//项目
{public string Name { get; set; }public double Price { get; set; }//项目价格
}
class Box//项目封装箱
{public Project Project {  get; set; }
}
class WrapFactory//封装工厂
{public Box WrapProject(IProjectFactory projectFactory){Box box = new Box();Project project = projectFactory.Make();box.Project = project;return box;}
}

更多推荐

C#学习

本文发布于:2023-11-16 11:58:18,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1620858.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!