基礎拾遺—委託,匿名函數,lambda

  • 2020 年 6 月 16 日
  • 筆記

前言:

  C# 中的委託(Delegate)類似於 C 或 C++ 中函數的指針。委託是存有對某個方法的引用的一種引用類型變數。引用可在運行時被改變。委託(Delegate)特別用於實現事件和回調方法。所有的委託都派生自 System.Delegate 類。把一個方法當作參數傳遞,讓其它方法進行調用執行。

1.委託的聲明

委託聲明決定了可由該委託引用的方法。委託可指向一個與其具有相同標籤的方法。

1.1.delegate

1.1.1. 0-23個參數,可以有返回值也可以沒有返回值

public delegate int MyDelegateEventHandler (string parm);

註:(1).此委託指向的方法必須是參數為string類型,返回類型為int類型的。其他聲明類比所得。

  (2).EventHandler是c# 命名規範,當然我理解規範就是可以隨意啦。

       (3).委託調用時必須判斷是否為null不然會報異常

       (4).事件也是一種委託

1.1.2.委託的調用

MyDelegateEventHandler fun=new MyDelegateEventHandler(method);
or
MyDelegateEventHandler fun=method;
// fun不為空,則調用回調方法
 if (fun!= null)
    {
         fun(val);
     }
 //fun?.Invoke(val); 簡化版本調用 

1.1.3.委託的多播

每個委託都只包含一個方法調用,如果調用多個方法,就需要多次顯示調用這個委託。如果同一個委託調用多個方法,我們就可以用多播委託

public delegate void MyDelegate ();

public voidMyMethod()
{
    //#
}
public void MyMethod1()
{
    //#
}
public void MyMethod2()
{
    //#
}
MyDelegateEnventHander myDelegate;
myDelegate=new MyDelegateEventHander(MyMethod);
myDelegate+=new MyDelegateEventHander(MyMethod1);
...........
//調用
myDelegate();

 註:

  1.委託對象可使用 “+” 運算符進行合併;

  2.”-” 運算符可用於從合併的委託中移除組件委託;

  3.委託指定方法類型必須一致;

  4.返回類型一般為void,但非必須;

  5.GetInvocationList獲取委託索引

if (MyDelegate != null) 
    System.Delegate[] dels = MyDelegate .GetInvocationList(); 
for (int i = 0; i < dels.Length; i++) 
  {
      MyDelegate  -= dels[i] as MethodDelegate;
   }     

以上是利用GetInvocationList獲取委託索引的一個簡單應用。

1.2.Action

 Action至少0個參數,至多16個參數,無返回值。

Action 表示無參,無返回值的委託
Action<int,string> 表示有傳入參數int,string無返回值的委託
Action<int,string,bool> 表示有傳入參數int,string,bool無返回值的委託
Action<int,int,int,int> 表示有傳入4個int型參數,無返回值的委託
 public void Test<T>(Action<T> action,T p)
        {
            action(p);
        }

1.3.Func

 Func至少0個參數,至多16個參數,根據返回值泛型返回。必須有返回值,不可void

Func是無返回值的泛型委託
Func<int> 表示無參,返回值為int的委託
Func<object,string,int> 表示傳入參數為object, string 返回值為int的委託
Func<object,string,int> 表示傳入參數為object, string 返回值為int的委託
Func<T1,T2,,T3,int> 表示傳入參數為T1,T2,,T3(泛型)返回值為int的委託

1.4.predicate

1.4.1.predicate 是返回bool型的泛型委託;

1.4.2.predicate<int> 表示傳入參數為int 返回bool的委託;

1.4.3.Predicate有且只有一個參數,返回值固定為bool;

public delegate bool Predicate<T> (T obj)

2.委託的實例化

2.1.delegate

public delegate int MyDelegateEventHandler (string parm)
public int MyMethod(string parm)
{
    //#
}
MyDelegateEventHandler MyDelegate=new MyDelegateEventHandler(MyMethod)

註:委託實例化的時候,委託對象必須使用 new 關鍵字來創建,且與一個特定的方法有關。委託參數中的方法不含參數。

2.2.Action的使用

 public void Test<T>(Action<T> action, T p)
  {
    action(p);
  }
 private void Action(string s)
 {
  #
 }
//調用
Test<string>(Action,"wyl");

2.3.Func的使用

public int Test<T1, T2>(Func<T1, T2, int> func, T1 a, T2 b)
{
 return func(a, b);
}
private int Fun(int a, int b)
{
  #
}
//調用
Test<int,int>(Fun,100,200)

2.4 委託實現冒泡排序

//定義對象
class Student
    {
        public string Name { get; private set; }
        public decimal Scores{ get; private set; }

        public Student(string name, decimal scores)
        {
            this.Name = name; this.Scores= scores;
        }
        public override string ToString()
        {
            return string.Format("{0},{1:C}",Name,Scores);
        }
        public static bool CompareScores(Student e1,Student e2)
        {
            return e1.Scores< e2.Scores;
        }
    }
//利用委託實現冒泡
class BubbleScores
    {
        public static void Sort<T>(IList<T> sortArray, Func<T, T, bool> comparison)
        {
            bool swapped = true;
            do
            {
                swapped = false;
                for (int i = 0; i < sortArray.Count - 1; i++)
                {
                    if (comparison(sortArray[i + 1], sortArray[i]))
                    {
                        T temp = sortArray[i];
                        sortArray[i] = sortArray[i + 1];
                        sortArray[i + 1] = temp;
                        swapped = true;
                    }
                }
            } while (swapped);
        }
    }
//調用
Student[] students={new Student("wyl", 100),#};
BubbleSorter.Sort(students, Student.CompareScores);
foreach(var student in students)
    Console.WriteLine(student);

3.匿名函數與lambda 

3.1什麼是匿名函數

匿名函數是一個「內聯」語句或表達式,可在需要委託類型的任何地方使用。

可以使用匿名函數來初始化命名委託(無需取名字的委託),或傳遞命名委託(而不是命名委託類型,傳遞一個方法塊,而不是委託類型)[callback的方式]作為方法參數。

MyDelegate funDelegate = delegate(string s) { Console.WriteLine(s); };
funDelegate ("this is anonymous delegate");

3.2.lambda

lambda表達式實際上是一個匿名函數。編譯器在看到lambda之後會在類中自動定義一個新的私有方法。lambda必須匹配委託!其中lambda是從c#3.0後引用的

lambda的語法:

參數 => 方法體。

=>左邊是要傳入的參數,本例中是傳入一個Int類型的變數,=>右邊是具體的程式碼。

//如果不傳遞參數:
()=>Console.WriteLine("Hello World!")
//傳遞一個參數:
(int n)=>Console.WriteLine(n.ToString())   
//或者去掉()和int  編譯器會自己推斷類型:
n=>Console.WriteLine(n.ToString())
//傳遞多個參數:
(int n ,int m)=>Console.WriteLine(n+m) 
//或者編譯器自己推斷類型:
(n , m)=>Console.WriteLine(m+n)

4.綜上:

4.1.委託類似於 C++ 函數指針。

4.2.委託允許將方法作為參數進行傳遞。

4.3.委託可用於定義回調方法。

4.4.委託可以鏈接在一起;多播。

4.5.方法不必與委託簽名完全匹配。