使用C#,但是大多数时候用不上委托,但是委托被讨论得非常多,于是陷入自我怀疑,我是不是太菜了?
我不禁问:为什么要使用委托?
可能试图了解过委托的同学,都被告知委托跟函数指针的关系,又或者被告知一个生活化的举例:委托就是委托别人执行一件事。
可能很多人尝试了解委托的时候都停在这一步,他们了解了委托的写法,寻思:委托也不能简化写法,而且做了和函数同样的事情,为什么要费劲巴拉的使用委托来间接完成函数调用呢?
想到这,我甚至有点生气,为什么他妈的要使用委托?
那就说说为什么要他妈的使用委托!
当你想把方法当参数传给另一个方法
-
1. 模板方法:Reuse,重复使用,也叫“复用”。代码的复用不但可以提高工作效率,还可以减少 bug 的引入。良好的复用结构是所有优秀软件所追求的共同目标之一。请仔细品味一下示例程序(示例代码来自刘铁锰老师《C#语言入门详解》系列课程):
class Program
{
static void Main(string[] args)
{
//实例化一个ProductFactory,此对象拥有多个成员方法,可被同一种委托类型调用
var productFactory = new ProductFactory();
//定义两个委托
Func<Product> func1 = new Func<Product>(productFactory.MakePizza);
Func<Product> func2 = new Func<Product>(productFactory.MakeToyCar);
var wrapFactory = new WrapFactory();
//将委托类型对象作为参数,传给装箱类的构造器
Box box1 = wrapFactory.WrapProduct(func1);
Box box2 = wrapFactory.WrapProduct(func2);
Console.WriteLine(box1.Product.Name);
Console.WriteLine(box2.Product.Name);
}
}
//第一个类:product类,具有一个string类型的属性Name,描述产品的名称
class Product
{
public string Name { get; set; }
}
//第二个类:box类,具有一个Product类型的属性,盒子中有一个product
class Box
{
public Product Product { get; set; }
}
//第三个类:装箱,构造器接收一个参数,参数为一个委托类型,该委托返回一个product类型的对象
//将参数返回的对象,放入box中,完成装箱
class WrapFactory
{
// 模板方法,提高复用性
public Box WrapProduct(Func<Product> getProduct)
{
var box = new Box();
Product product = getProduct.Invoke();
box.Product = product;
return box;
}
}
//第四个类:product制造工厂,容纳制造各种product的方法,方法数量可依需求扩展
//但特点是返回值均为Product类型,无参数,即可被同一种委托类型调用
class ProductFactory
{
public Product MakePizza()
{
var product = new Product();
product.Name = "Pizza";
return product;
}
public Product MakeToyCar()
{
var product = new Product();
product.Name = "Toy Car";
return product;
}
}
看完上述程序总结:
我们用了4个类,Product、Box、WrapFactory、ProductFactory,product为操作对象,box为product的装箱容器,WrapFactory专门执行装箱操作,ProductFactory容纳各种product制造函数,此类为可扩展类,异变更。最终在main方法中,在装箱时使用委托作为参数,即 wrapFactory.WrapProduct(func1) 这句代码,我想为什么不直接wrapFactory.WrapProduct(productFactory.MakePizza),不是更方便吗?当我继续往下想,**如果不光是使用参数中的返回值呢,加入作为参数的方法除了返回值,还有out类型的输出参数呢,这时候就不得不使用委托类型作为函数参数了。**而且有没有莫名觉得上面的示例程序结构非常棒棒,当新增一个种类的Product,指需扩展ProductFactory类的成员,并在main方法中增加一个委托,然后装箱即可。这就是我们为什么要使用委托的第一个原因。所以请熟读并模型示例程序!
-
2. 回调方法:回调方法是通过委托类型参数传入主调方法的被调用方法,主调方法根据自己的逻辑决定是否调用这个方法。请仔细品味一下示例程序(示例代码来自刘铁锰老师《C#语言入门详解》系列课程):
class Program
{
static void Main(string[] args)
{
var productFactory = new ProductFactory();
// Func 前面是传入参数,最后一个是返回值,所以此处以 Product 为返回值
Func<Product> func1 = new Func<Product>(productFactory.MakePizza);
Func<Product> func2 = new Func<Product>(productFactory.MakeToyCar);
var wrapFactory = new WrapFactory();
var logger = new Logger();
// Action 只有传入参数,所以此处以 Product 为参数
Action<Product> log = new Action<Product>(logger.Log);
Box box1 = wrapFactory.WrapProduct(func1, log);
Box box2 = wrapFactory.WrapProduct(func2, log);
Console.WriteLine(box1.Product.Name);
Console.WriteLine(box2.Product.Name);
}
}
class Logger
{
public void Log(Product product)
{
// Now 是带时区的时间,存储到数据库应该用不带时区的时间 UtcNow。
Console.WriteLine("Product '{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 WrapFactory
{
// 模板方法,提高复用性
public Box WrapProduct(Func<Product> getProduct, Action<Product> logCallBack)
{
var box = new Box();
Product product = getProduct.Invoke();
// 只 log 价格高于 50 的
if (product.Price >= 50)
{
logCallBack(product);
}
box.Product = product;
return box;
}
}
class ProductFactory
{
public Product MakePizza()
{
var product = new Product
{
Name = "Pizza",
Price = 12
};
return product;
}
public Product MakeToyCar()
{
var product = new Product
{
Name = "Toy Car",
Price = 100
};
return product;
}
}