天天看點

為什麼要使用委托當你想把方法當參數傳給另一個方法看完上述程式總結:總結:委托易使用,難精通,慎用委托。

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

總結:委托易使用,難精通,慎用委托。