天天看点

为什么要使用委托当你想把方法当参数传给另一个方法看完上述程序总结:总结:委托易使用,难精通,慎用委托。

使用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;
    }
}
           

总结:委托易使用,难精通,慎用委托。