从三个维度学习委托(根据刘铁猛老师做的笔记)
-
C#中自带的委托
-
自定义委托的学习
-
委托的应用
一、C#自带的委托
1.程序的本质是数据加算法
变量(数据)的本质:以变量名最对应的内存地址为起点的一段内存,在这段内存中存储的就是变量的数据,内存多大就是有数据类型决定的。
函数(算法):也是地址,是函数名所对应的内存地址为起点的一段内存,这段内存不是一个值,而是存储的一组机器语言指令。
无论是数据还是算法,都是保存在内存中。变量是用来寻找数据的地址,函数是用来寻找算法的地址。
2.直接调用与间接调用的本质:
直接调用:通过函数名来调用函数,CPU通过函数名直接获取函数所在地址开始执行,直到最后一行然后返回到调用的位置。
间接调用:通过指向某个函数的函数指针来调用函数,函数指针是指向函数的一个指针,函数指针所存储的的就是函数名所对应的地址。通过函数指针,CPU一样可以找到存储那段机器语言的内存。
使用C#自带的委托-示例:
- Action:无返回值的委托使用Action
- Func:有返回值的委托使用Func
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp4
{
class Program
{
static void Main(string[] args)
{
//创建实例
Calculator calculator = new Calculator();
//创建action 数据类型
//C#中已有的委托 -》action ;在此间接调用只是需要report方法的方法名,不需要加括号
Action action = new Action(calculator.Report);
//Report方法的直接调用
calculator.Report();
//report方法的间接调用
action.Invoke();
//可以指向add与sub 方法的委托=> function
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 =300;
int y = 100;
int z;
//Add方法的间接调用
z = func1.Invoke(x, y);
Console.WriteLine(z);
//Sub方法的间接调用
z = func2.Invoke(x, y);
Console.WriteLine(z);
}
}
class Calculator
{
public void Report()
{
Console.WriteLine("i have 3 methods ");
}
public int Add(int x, int y)
{
int result = x + y;
return result;
}
public int Sub(int x, int y)
{
int result = x - y;
return result;
}
}
}
在使用Func委托根据我们写的方法,可以看到func的补充:传入为两个int,out也是int。因此:add方法与sub方法是符合的。
二、自定义委托
委托是一种类,类是一种数据类型(引用类型的数据类型)。
用类可以声明变量,可以创建实例。
所以委托可以声明变量,创建实例.
委托是一种类,要声明在命名空间中,这样就可以跟其他的类处在同一个级别了。
下面声明委托(自定义委托)-示例:
using System.Data.SqlTypes;
using System.Runtime.Intrinsics;
namespace ConstructorExample
{
//声明委托
public delegate double Cal(double x, double y); //第一个double为目标方法的返回值
//Cal 为委托名,括号内为目标方法的参数列表
class program
{
static void Main()
{
//首先创建Calculator类的实例,有了这个实例,我们才可以访问这个实例的方法
Calculator calculator = new Calculator();
Cal cal1 = new Cal(calculator.Add); //此时传入的方法必须满足返回值为double并且参数列表为两个double
//calculator.Add不加括号是因为此处间接访问方法,与直接访问不同。
Cal cal2 = new Cal(calculator.Sub);
Cal cal3 = new Cal(calculator.Mul);
Cal cal4 = new Cal(calculator.Div);
double a = 10;
double b = 10;
double c;
c= cal1.Invoke(a, b);
Console.WriteLine(c);
c= cal2.Invoke(a, b);
Console.WriteLine(c);
c= cal3.Invoke(a, b);
Console.WriteLine(c);
c= cal4.Invoke(a, b);
Console.WriteLine(c);
}
}
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;
}
}
}
注:如果委托声明在其他program类中,编译、运行都可以正常进行。但是Cal委托就不是一个独立的类了,而是program的嵌套类。
如下:(在声明cal1,2,3,4是前面的program.Cal也可以直接写为Cal,因为Cal本身就写在了program中)
要注意的是:声明的委托要与它所封装的方法(所指向的目标方法)必须保持类型兼容。
类型兼容就是声明委托的时候所使用的的目标方法的返回值类型,以及目标方法的 参数列表类型必须要与目标方法的返回值类型和参数列表一致。
三:委托的使用
在正常的使用中,我们都会把委托当做方法的参数传进方法中。这么做的好处就是,当你写了一个方法,这个方法有一个委托类型的参数(委托会封装着一个方法),当我们在方法体中使用传进来的委托,间接的调用委托所封着的那个方法。这样就会形成动态调用方法的代码结构。
把委托当做参数传进方法的用法有两种:
- 模版方法
- 回调方法
1.模版方法:
当你写了一个方法,通过传进来的委托参数,借用指定的外部方法,来产生一个结果。(相当于在写的方法中有一个填空题,这个填空题的空白处就用传进来的委托参数间接的调用指定的外部方法)。 这个方法一般是有返回值的,当拿到返回值后,继续执行方法后的逻辑。
就类似于你写了一个模版,这个模版有一处是不确定的,其余部分是确定好的。这个不确定的部分就通过传进来的委托类型的参数所包含的方法进行填补。
下面是不使用委托的示例(只是方法简单的调用):
using System.Data.SqlTypes;
using System.Runtime.Intrinsics;
namespace DelegateExample
{
class Program
{
static void Main(string[] args)
{
WrapFactory wrapFactory = new WrapFactory();
Box box = wrapFactory.WrapProduct();
Console.WriteLine(box.Product.Name);
}
}
class Product //定义产品属性
{
public string Name { get; set; }
}
class Box //定义包装盒,用于包装产品
{
public Product Product { get; set; }
}
class MakeProduct //定义生产工厂,用于生产产品Product
{
public Product MakePizza()
{
Product product = new Product();
product.Name = "Pizaa";
return product;
}
public Product MakeToyCar()
{
Product product = new Product();
product.Name = "ToyCar";
return product;
}
}
class WrapFactory //定义包装工厂,将生产工厂MakeProduct的产品Product包装到包装盒Box中
{
public Box WrapProduct()
{
MakeProduct makeProduct1 = new MakeProduct();
Product product = makeProduct1.MakePizza();
Box box = new Box();
box.Product = product;
return box;
}
}
}
下面为使用委托的用例:
using System.Data.SqlTypes;
using System.Runtime.Intrinsics;
namespace DelegateExample
{
class Program
{
static void Main(string[] args)
{
MakeProduct makerProduct = new MakeProduct();
Func<Product> func1 = new Func<Product>(makerProduct.MakePizza);
Func<Product> func2 = new Func<Product>(makerProduct.MakeToyCar);
WrapFactory wrapFactory = new WrapFactory();
Box box1 = wrapFactory.WrapProduct(func1);
Console.WriteLine(box1.Product.Name);
Box box2 = wrapFactory.WrapProduct(func2);
Console.WriteLine(box2.Product.Name);
}
}
class Product //定义产品属性
{
public string Name { get; set; }
}
class Box //定义包装盒,用于包装产品
{
public Product Product { get; set; }
}
class MakeProduct //定义生产工厂,用于生产产品Product
{
public Product MakePizza()
{
Product product = new Product();
product.Name = "Pizaa";
return product;
}
public Product MakeToyCar()
{
Product product = new Product();
product.Name = "ToyCar";
return product;
}
}
class WrapFactory //定义包装工厂,将生产工厂MakeProduct的产品Product包装到包装盒Box中
{
public Box WrapProduct(Func<Product> getProduct)//(Func<Product> getProduct封装一个方法,方法的返回值类型为Product
{
Product product =getProduct.Invoke(); //getProduct.Invoke()指向委托封装的方法所在的位置
Box box = new Box();
box.Product = product;
return box;
}
}
}
可以通过上述对来来看使用委托封装方法传入方法与直接调用方法的区别。可以看出来使用委托更加方便。可重点查看Main 与class WrapProduct 来对比:
2.回调方法:
回调(Call back),回调指的一种场景是:有一天我遇到了A,我把名片给A,并告知A需要我的时候给我打电话,如果需要我的时间,A会call 我,不需要我的时间,A永远不会call 我。这就类似于在代码中A调用我一样,如果A需要我就会调用我,不需要我就永远不需要调用我。这样就构成了回调方法的关系。(而且回调方法给我们一个机会,我们可以动态的选择后续将被调用的方法。)
当以回调方法的形式来使用委托的时候,我们要做的是把委托类型的参数传入主调方法里面去,被传入主调方法中的委托类型的参数里面会封装着被回调的方法,主调函数会根据自己的逻辑觉得要不要调用回调方法。整个代码的逻辑像一个流水线,一般情况下,主调方法会在主要逻辑执行完之后决定要不要调用回调方法。
回调方法一般位于主调方法代码的末尾,而且回调方法一般的用处是执行后续的工作,构成一个流水线的结果,所以说回调方法一般是没有返回值的。
回调方法的应用:
using System.Data.SqlTypes;
using System.Runtime.Intrinsics;
namespace DelegateExample
{
class Program
{
static void Main(string[] args)
{
MakeProduct makerProduct = new MakeProduct();
Func<Product> func1 = new Func<Product>(makerProduct.MakePizza);
Func<Product> func2 = new Func<Product>(makerProduct.MakeToyCar);
//使用回调方法
Logger logger = new Logger();
Action<Product> ac1 = new Action<Product>(logger.log);
WrapFactory wrapFactory = new WrapFactory();
Box box1 = wrapFactory.WrapProduct(func1,ac1);
Console.WriteLine(box1.Product.Name);
Box box2 = wrapFactory.WrapProduct(func2,ac1);
Console.WriteLine(box2.Product.Name);
}
}
class Logger
{
public void log(Product product)
{
Console.WriteLine("Product {0} create at {1} .Price is {2}.",product.Name,DateTime.UtcNow,product.Price);
}
}
class Product //定义产品属性
{
public string Name { get; set; }
public double Price { get; set; }
}
class Box //定义包装盒,用于包装产品
{
public Product Product { get; set; }
}
class MakeProduct //定义生产工厂,用于生产产品Product
{
//模版方法为MakePizza、MakeToyCar
public Product MakePizza()
{
Product product = new Product();
product.Name = "Pizaa";
product.Price = 20;
return product;
}
public Product MakeToyCar()
{
Product product = new Product();
product.Name = "ToyCar";
product.Price = 190;
return product;
}
}
class WrapFactory //定义包装工厂,将生产工厂MakeProduct的产品Product包装到包装盒Box中
{
//(Func<Product> getProduct封装一个方法,方法的返回值类型为Product
//Action<Product> logCallBack 为无返回值的委托 传入参数类型为Product
public Box WrapProduct(Func<Product> getProduct,Action<Product> logCallBack)
{
Product product =getProduct.Invoke(); //getProduct.Invoke()指向委托封装的方法所在的位置
if (product.Price > 50)
{
logCallBack(product); //使用回调方法
}
Box box = new Box();
box.Product = product;
return box;
}
}
}
标签:Product,委托,C#,product,使用,new,方法,public
From: https://blog.csdn.net/YCG8882888/article/details/144619904