1. 程式人生 > 其它 >【寫給Cpp選手的C#教程】委託篇

【寫給Cpp選手的C#教程】委託篇

委託的簡單使用

本人對委託的理解:C中的函式指標。用一個變數儲存函式,方便傳遞和使用。

按照如下方法使用:

delegate int Dele(int a);
class Program
{
    static int pow(int a) { return a * a; }
    static void Main(string[] args)
    {
        //全寫為dele myPow = new dele(pow);
        Dele myPow = pow;
        //全寫為myPow.Invoke(3);
        myPow(3);
    }
}

很容易想到,我們可以往函式中傳入委託,進行解耦。

引數或返回值為委託的函式被稱為高階函式(high-order function)

delegate int Dele(int x);
class Program
{
    static int Square(int x) { return x * x; }

    static void Main()
    {
        int[] values = { 1, 2, 3 };
        Transform(values, Square);
    }
    //Transform就是一個高階函式
    static void Transform(int[] values, Dele t)
    {

        for (int i = 0; i < values.Length; i++)
            //values[i] = Square(values[i])
        { values[i] = t(values[i]); }
    }
}

多播委託

我們的委託可以存放一個函式列表,呼叫時呼叫整個列表。本質是new一個委託物件,然後重新賦值。

delegate int NumDele(int a);
class Program
{
    static int Pow2(int a) { return a * a; }
    static int Pow3(int a) { return a * a * a; }
    static int Pow4(int a) { return a * a * a * a; }​
        static void Main()
    {
        NumDele d = null;    
        //往函式列表中增加新的函式  
        d += Pow2; d += Pow3; d += Pow4;    
        //現在d中的內容:Pow2->Pow3->Pow4

        //刪除指定函式 
        d -= Pow3;
        //現在d中的內容:Pow2->Pow4

        Console.Write(d(3));
        //返回值為3 * 3 * 3 * 3 = 81,Pow2的返回值被丟棄
    }
}

委託和物件

你若細看便會發現,上面給委託賦值的函式全部都是static靜態函式。

如果我們將例項A的函式F賦值給委託C,那麼委託C不僅需要考慮函式F,還需要考慮例項A

可以通過如下方法獲取例項:

delegate int NumDele(int a);
class Source
{
    public int Pow2(int a) { return a * a; }
}
class Program
{
    static void Main()
    {
        Source s = new Source();
        NumDele d = s.Pow2;

        Console.WriteLine(d.Target);//TestSharp.Source
        Console.WriteLine(d.Target == s);//True
        Console.WriteLine(d.Method);//Int32 Pow2(Int32)
    }
}

委託和泛型

委託可以包括泛型型別引數:

public delegate T Dele<T>(T prop);

之後我們就可以進行一些諸如Cpp中algorithm庫中的一些,泛型演算法的操作(不過下面這個例子並不是泛型演算法)

public delegate T Dele<T>(T val);

class Program
{
    static int square(int x) { return x * x; }
    static double devide(double x) { return x / 2; }
    
    static void changeArray<T>(T[] arr, Dele<T> opt)
    {
        for (int i = 0; i < arr.Length; i++)
            arr[i] = opt(arr[i]);
    }
    
    static void Main()
    {
        int[] iArr = { 1, 2, 3 };
        double[] dArr = { 5.4, 2.7, 9.8 };
        //對於int型別的陣列,使用square函式對其進行加工
        changeArray(iArr, square); //iArr變成了[1 , 4 , 9]
        //對於double型別的陣列,使用devide函式對其進行加工
        changeArray(dArr, devide); //dArr變成了[2.7 , 1.35 , 4.9]
    }
}

如果我們經常需要用到泛型委託,然後每次都需要自己進行定義和宣告,那很明顯很煩人。後面C#很貼心的減少了我們的套路活,整出了Func<>和Action<>。

對於Func<T1,T2,T3...>而言,最後一個T是返回值,其餘的T都是引數。

對於Action<T1,T2,T3...>而言,所有的T都是引數。

//public delegate T Dele<T>(T val);
//static void changeArray<T>(T[] arr, Dele<T> opt)
static void changeArray<T>(T[] arr,Func<T,T> opt) //使用Func的寫法,就不需要宣告Dele了

委託的相容性

①委託的型別不同,不能賦值

②委託指向相同函式,則認為其是等價的

public delegate void D1();
public delegate void D2();

class Program
{
    static void Func() {}
    static void Main()
    {
        D1 d1 = Func;
        //D2 d2 = d1;     	如果這麼寫會報錯
        D2 d2 = new D2(d1); //這麼寫才行
        
        D1 d3 = Func;
        D2 d4 = Func;
        d3==d4  			//True
    }
}

③跟委託繫結的函式,其引數可以是委託的引數的父類、子類

public delegate void StringDele(string s);

class Program
{
    static void ObjectFunc(object o) { }
    static void Main()
    {
        StringDele s = ObjectFunc;
        s("hello world"); //string引數會隱式變換為object引數
    }
}

一般的,傳子類是正常的多型行為。傳父類被稱為逆變。

④跟委託繫結的函式,其返回值可以是委託的返回值的父類、子類

public delegate object objectDele();

class Program
{
    static string strFuc() { return "hello"; }
    static void Main()
    {
        objectDele s = strFuc;
        s();
    }
}

objectDele想要返回object,但如果繫結的函式返回object的子類,那麼也是可以的。

函式的返回值是委託返回值的子類,這種情況被稱為協變。

⑤泛型委託相關

我們需要將只用於返回值的型別引數標記為協變(out),將只用於引數的型別引數標記為逆變(in)

其實,Func和Action的定義可以如下理解:

delegate TResult Func<in T1,in T2,...,out TResult>(T1 prop1,T2 prop2,...);
delegate void Action<in T1,in T2,...>(T1 prop1,T2 prop2,...);

然後吧,我們的泛型委託也可以如同之前定義的委託那樣,完成逆變和協變了。