概念介绍
C++/C函数指针
C#委托是C/C++中 函数指针的升级版。C++函数指针本质是一串32位/64位的地址,函数指针是指向函数的指针,可以借此指针调用函数,也可以用指针指向另一个函数。函数指针举例如下:
#include <iostream>
using namespace std;
typedef int (*cal)(int a, int b);//函数指针:返回值类型+指针名称+参数列表
int Add(int a, int b);//函数声明
int main(void)
{
cal funtion11 = &Add;//函数指针指向ADD函数,也可以指向其他函数
int sum = funtion11(1,2);
cout << "FUNCIOTN IS "<<sum << endl;
}
int Add(int a, int b)
{
int result = a + b;
return result;
}
可以看出函数指针可以指向任何符合定义类型的函数。这是一种间接调用方式。
直接调用:直接调用函数名。
间接调用:通过函数函数指针调用函数。
C#委托
为什么委托是函数指针的升级版?
委托的声明和调用类似于函数指针,并且C#是基于C++设计的,因此委托是函数指针的升级版。
C#基本的委托有两种:Action、Func,这无须开发者自己定义。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading.Tasks;
namespace Delegate
{
internal class Program
{
static void Main(string[] args)
{
Calculater Calculater = new Calculater();
Action action = new Action(Calculater.Report);//Action委托指向无返回值的report方法
action();//间接调用
Func<int/*返回值类型*/, int/*函数参数1*/, int/*函数参数2*/> func = new Func<int, int, int>(Calculater.Add);//Function委托指向有返回值的函数
int result = func(1,2);
Console.WriteLine(result);
}
}
class Calculater
{
public void Report() => Console.WriteLine("Hello world!");
public int Add(int a, int b)
{
int result =a + b;
return result;
}
}
}
如何自定义委托?
委托实际是一种类,您可以执行以下语句,结果返回True。另外,如果您使用的是VS Code开发,可以看到Action与Func的颜色为水蓝色,这代表一种数据类型。
Type type = typeof(Action);
既然是类,这代表开发者可以自定义委托。举例如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading.Tasks;
namespace Delegate
{
public delegate int/*返回值类型*/ Calc(int a/*参数1*/, int b/*参数2*/);//自定义委托
internal class Program
{
static void Main(string[] args)
{
Calculater Calculater = new Calculater();
Calc calc = new Calc(Calculater.Add);//新建委托
int res = calc(1,2);
Console.WriteLine(res);
}
}
class Calculater
{
public int Add(int a, int b)
{
int result =a + b;
return result;
}
}
}
- 自定义委托返回的数据类型必须和指向的函数一致。
- 自定义委托的参数个数和数据类型必须和指向的函数一致。
- 委托的声明必须声明在名称空间中,和其他类同级。
委托的一般使用
C#委托意义同C++函数指针的意义,一般都是将委托当作参数传递给另一个方法。有Template方法和CallBack方法两种。
Template方法
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading.Tasks;
namespace Delegate
{
public delegate int Calc(int a, int b);//自定义委托
internal class Program
{
static void Main(string[] args)
{
ProductFactory productFactory = new ProductFactory();//创建一个产品工厂
WrapFactory wrapFactory = new WrapFactory();//创建一个包装工厂
//创建委托
Func<Product/*返回值类型*/> fun1 = new Func<Product>(productFactory.MakeCake/*满足返回值类型的方法*/);
//将委托作为参数输入给包装工厂
Box box1 = wrapFactory.WarpProduct(fun1);//输入指向产品的委托
Console.WriteLine( box1.Product.name);
}
}
class Product//产品制作
{
public string name { get; set; }
}
class Box//包装盒制作
{
public Product Product { get; set; }
}
class ProductFactory//蛋糕生产
{
public Product MakeCake()
{
Product product = new Product();
product.name = "Da_Li_Yuan";
return product;
}
}
class WrapFactory //产品包装
{
public Box WarpProduct(Func<Product> getProduct)/*委托作为函数参数,意味着必须输入一个指向产品的委托*/
{
Box box = new Box();
Product product = getProduct();//调用委托
box.Product = product;
return box;
}
}
}
上述这种方法就是模板方法:调用通用的外部方法,有返回值。只要满足Func<Product> getProduct,都可以传入方法。
CallBack方法
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading.Tasks;
namespace Delegate
{
public delegate int Calc(int a, int b);//自定义委托
internal class Program
{
static void Main(string[] args)
{
ProductFactory productFactory = new ProductFactory();//创建一个产品工厂
WrapFactory wrapFactory = new WrapFactory();//创建一个包装工厂
Logger logger = new Logger();
Func<Product/*返回值类型*/> fun1 = new Func<Product>(productFactory.MakeCake/*满足返回值类型的方法*/);
Action<Product> log1 = new Action<Product>(logger.Log);
Box box1 = wrapFactory.WarpProduct(fun1,log1);//输入指向产品的委托,输入log函数
Console.WriteLine( box1.Product.name);
}
}
class Logger//记录程序的运行状态
{
public void Log(Product product)
{
Console.WriteLine("Produce {0} created 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 ProductFactory//蛋糕生产
{
public Product MakeCake()
{
Product product = new Product();
product.name = "Da_Li_Yuan";
product.Price = 100;
return product;
}
}
class WrapFactory //产品包装
{
public Box WarpProduct(Func<Product> getProduct,Action<Product> logCallBack)/*委托作为函数参数,意味着必须输入一个指向产品的委托*/
{
Box box = new Box();
Product product = getProduct();//调用委托
if(product.Price>=50)
logCallBack.Invoke(product);
box.Product = product;
return box;
}
}
}
上述方法就是回调方法:调用指定的外部方法,无返回值。比如这里的log。
注意事项
- 委托是一种方法级别的高度耦合,使用时请慎之又慎!
- 委托使可读性降低,debug难度增加
- 委托和多线程,异步调用,委托回调纠缠在一起时,请慎之又慎!
- 委托使用不当将导致内存泄漏和性能下降。
委托的高级使用
多播委托
多播委托类似于Someip的多播,一个委托内部有多个委托是使用。
隐式异步调用
结语
小张非常建议测试开发工程师业余时花费3个月左右时间将C++/C/C#三种语言的基础都过一遍,业余再花费1-2年时间将一到两种语言彻底熟练,这将建立工程师代码的基本的思维,继而开发出合理简洁的代码。学习各类语言的特点对于测试框架的开发十分有必要。熟悉各类语言的语言后,可以针对学习基于各类语言在日常工作的开发工具链,例如C#的WPF开发与上位机开发,C++的DLL开发。
作为测试工程师,个人建议代码方面主攻C#和Pyhton。
附
我们可以看一段糟糕的代码:
是不是感觉还好?只是进行了一次套用,那我再拿出如下代码,阁下如何应对?


