C#委託

C#委託

23考完復變打算更博的,可是那天頂着38.5°C考完復變以後實在頂不住了,歇了幾天算法

委託

委託(delegate)是C/C++裏面函數指針的升級版,若是你有必定的C基礎,那就知道委託的做用了。在計算機裏面一切皆爲地址,變量(數據)是以某個地址爲起點的一段內存中所存儲的值,函數(算法)是以某個地址爲起點的一段內存中所存儲的一組機器語言指令。函數的調用有兩種方法:直接調用和間接調用,直接調用就是直接經過函數的名字來調用,CPU經過函數的名字找到函數存儲的位置進而執行;間接調用是用函數指針來調用,CPU經過函數指針存儲的地址找到函數所在的地方進而執行。
C#中的委託是通過包裝的函數指針,讓你感覺不到它是一個指針。可是實際用起來,就是函數指針的用法。C#中的委託能夠本身寫一個,或者使用C#爲你準備好的一些委託,好比Action(無返回值的函數)、Func(有返回值的函數)。
說了這麼多,委託有什麼用呢?拿兩個經典例子來講:模板方法(template method)和回調方法(callback)。
多說無益,看例子:多線程

模板方法

using System;

namespace SomeNote
{
    class Program
    {
        static void Main(string[] args)
        {
            ProductFactory productFactory = new ProductFactory();
            WrapFactory wrapFactory = new WrapFactory();
            Box box1 = new Box();
            Box box2 = new Box();

            Func<Product> fun1 = productFactory.MakePizza;             //Func這個委託是C#準備好的,它接受一個帶返回值,可是沒有參數的函數
            Func<Product> fun2 = productFactory.MakeToyCar;            //Func是一個泛型委託,尖括號裏面是它的返回值類型

            box1 = wrapFactory.WrapProduct(fun1);
            box2 = wrapFactory.WrapProduct(fun2);

            Console.WriteLine(box1.Product.Name);
            Console.WriteLine(box2.Product.Name);
        }
    }

    class Product
    {
        public string Name { get; set; }
    }

    class Box
    {
        public Product Product { get; set; }
    }

    class WrapFactory
    {
        public Box WrapProduct(Func<Product> getProduct)             //一個模板方法,接收一個委託,這個委託在方法體裏面產生某個結果
        {                                                            //模板方法至關於填空題,你須要什麼就放什麼進參數表
            Box box = new Box();
            Product product = getProduct.Invoke();
            box.Product = product;
            return box;
        }
    }

    class ProductFactory
    {
        public Product MakePizza()
        {
            Product product = new Product();
            product.Name = "Pizza";
            return product;
        }

        public Product MakeToyCar()
        {
            Product product = new Product();
            product.Name = "Toy Car";
            return product;
        }
    }
}

這就是模板方法,借用指定的外部方法來產生結果。WrapProduct這個方法是很是好的一個處理方式,你須要什麼就給它什麼,模板方法通常出如今代碼中間。模板方法的委託通常有返回值,放到方法體裏面作各類處理。
程序的執行結果以下:
模板方法異步

回調方法

using System;

namespace SomeNote
{
    class Program
    {
        static void Main(string[] args)
        {
            WrapFactory wrapFactory = new WrapFactory();
            ProductFactory productFactory = new ProductFactory();
            Box box1 = new Box();
            Box box2 = new Box();
            Logger logger = new Logger();

            Action<Product> action = new Action<Product>(logger.Log);                 //Action也是C#爲咱們準備好的一個泛型委託
            Func<Product> func1 = new Func<Product>(productFactory.MakePizza);        //它接收一個沒有返回值,參數只有一個的函數,尖括號裏面是函數的參數類型
            Func<Product> func2 = new Func<Product>(productFactory.MakeToyCar);       //不知道你有沒有發現這裏聲明委託和上面的例子不同

            box1 = wrapFactory.WrapProduct(func1, action);
            box2 = wrapFactory.WrapProduct(func2, action);

            Console.WriteLine(box1.Product.Name);
            Console.WriteLine(box2.Product.Name);
        }
    }

    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)    //多了一個參數
        {
            Box box = new Box();
            Product product = getProduct.Invoke();

            if (product.Price >= 50)                                                     //價格大於50就輸出它的價格
            {
                logCallback(product);
            }

            box.Product = product;
            return box;
        }
    }

    class ProductFactory
    {
        public Product MakePizza()
        {
            Product product = new Product();
            product.Name = "Pizza";
            product.Price = 60;
            return product;
        }

        public Product MakeToyCar()
        {
            Product product = new Product();
            product.Name = "Toy Car";
            product.Price = 30;
            return product;
        }
    }

    class Logger                                                                     //這個類只有一個方法,用來實現回調
    {
        public void Log(Product product)
        {
            Console.WriteLine(product.Price);
        }
    }
}

回調方法的例子我只是在模板方法的基礎上稍做修改。注意到WrapProduct這個方法多了一個參數,多接收了一個委託。在方法體裏面,多了一個判斷價錢是否大於50,這裏的ActionLog,輸出Product的價格。回調方法至關於一條流水線,你符合就調用,不符合就不調用,它經常位於代碼末,它使用的委託沒有返回值。
最後關注一下1五、1六、17行的代碼,與模板方法的例子不同,這裏委託的聲明一大串,很複雜。這是什麼意思呢?在上一個例子,咱們沒有說明Func綁定的函數是什麼類型的,這是由於編譯器會幫咱們作推斷。而這個例子咱們把它說清楚了,它就是一個Func<Product>型的委託,綁定的函數是MakePizza。其實編譯器很聰明的,通常咱們不用完整說出來它的類型,編譯器會幫咱們推斷。
最終,程序的運行結果:
回調方法函數

Caution!

委託有一個很突出的特色:難精通、易使用、功能強大。委託濫用會產生不少很差的後果:性能

  • 這是一種方法級別的緊耦合,現實工做中要謹慎使用
  • 使可讀性降低,debug難度大大增長
  • 把委託回調、異步調用和多線程糾纏在一塊兒,會讓代碼難以閱讀和維護
  • 委託使用不當可能形成內存泄露和程序性能降低

In a word,委託是好東西,可是用以前要三思。spa

碎碎念

由於複習復變,一個星期沒寫碼了。昨天頂着38度作了博客園客製化,侯捷的課還剩下3節。我以爲我挺拼的,可是好像沒什麼結果。下週工做室申請那邊要答辯了,但願一切順順利利吧,爲了R社,也爲了本身。(其實今天這篇博客也是受狒狒啓發才寫的)線程

相關文章
相關標籤/搜索