簡體   English   中英

C#(或其他語言)中代理人的使用

[英]Uses of delegates in C# (or other languages)

我一直想知道代表們如何有用,我們為什么要使用它們? 除了類型安全和Visual Studio文檔中的所有這些優點之外,代表的真實用途是什么。

我已經找到了一個,它非常有針對性。

using System;

namespace HelloNamespace {

    class Greetings{
    public static void DisplayEnglish() {
            Console.WriteLine("Hello, world!");
    }
    public static void DisplayItalian() {
             Console.WriteLine("Ciao, mondo!");
    }
    public static void DisplaySpanish() {
             Console.WriteLine("Hola, imundo!");
         }
    }

    delegate void delGreeting();

    class HelloWorld {
        static void Main(string [] args) {

    int iChoice=int.Parse(args[0]);
    delGreeting [] arrayofGreetings={
             new delGreeting(Greetings.DisplayEnglish),
             new delGreeting(Greetings.DisplayItalian),
             new delGreeting(Greetings.DisplaySpanish)};

    arrayofGreetings[iChoice-1]();
         }
    }
}

但這並沒有完全顯示使用委托的優勢,而不是一個條件“If ... {}”來解析參數並運行方法。

有誰知道為什么在這里使用委托更好,而不是“if ... {}”。 您是否還有其他示例來證明代表的有用性。

謝謝!

代理是將功能注入方法的好方法。 因此,它們極大地幫助了代碼重用

考慮一下,假設你有一組相關的方法具有幾乎相同的功能,但只是幾行代碼。 您可以將這些方法共有的所有內容重構為一個方法,然后您可以通過委托注入專用功能。

以LINQ使用的所有IEnumerable擴展方法為例。 所有這些都定義了常用功能,但需要傳遞給它們的委托來定義如何投影返回數據,或者如何過濾,排序等數據......

在C#中我能想到的最常見的代表日常使用是事件處理。 如果在WinForm上有一個按鈕,並且想要在單擊按鈕時執行某些操作,那么您所做的就是在按下按鈕時最終注冊一個委托函數。

所有這些都是在Visual Studio自身生成的代碼中自動發生的,因此您可能看不到它發生的位置。

對您來說可能更有用的真實案例是,如果您想創建一個人們可以使用的庫,它將從Internet Feed中讀取數據,並在更新Feed時通知他們。 通過使用委托,使用您的庫的程序員可以在更新訂閱源時調用自己的代碼。

Lambda表達式
代表們大多與事件一起使用。 但動態語言顯示出更廣泛的用途。 這就是為什么當我們得到Lambda表達式時,代表們在C#3.0之前沒有得到充分利用。 使用Lambda表達式(生成委托方法)很容易做某事

現在假設你有一個IEnumerable字符串。 您可以輕松定義委托(使用Lambda表達式或任何其他方式)並將其應用於在每個元素上運行(例如修剪多余的空格)。 並且不使用循環語句就這樣做。 當然,你的代表可能會做更復雜的任務。

我將嘗試列出一些超出簡單的if-else場景的示例:

  1. 實施回電。 例如,您正在解析XML文檔,並希望在遇到特定節點時調用特定函數。 您可以將委托傳遞給函數。

  2. 實施戰略設計模式。 將委托分配給所需的算法/策略實現。

  3. 如果您希望在單獨的線程上執行某些功能,則匿名委托(此函數沒有任何內容可以發送回主程序)。

  4. 其他人建議的活動訂閱。

代表只是.Net的第一類函數的實現,並允許使用它們的語言提供高階函數

這種風格的主要好處是可以將公共方面抽象為一個函數,該函數完成它需要做的事情(例如遍歷數據結構)並提供另一個函數(或函數),它要求做某事隨着它的進展。

規范的功能示例是mapfold ,可以通過提供一些其他操作來改變以執行各種事情。

如果你想要對T的列表求和並且有一些函數add需要兩個T並將它們加在一起然后(通過部分應用) fold add 0變為sum。 fold multiply 1將成為產品, fold max 0為最大值。 在所有這些示例中,程序員無需考慮如何迭代輸入數據,無需擔心輸入為空時該怎么做。

這些都是簡單的例子(雖然它們與其他組合時可能會出奇的強大),但考慮樹遍歷(一個更復雜的任務),所有這些都可以在treefold函數后面抽象出來。 編寫樹折功能可能很難,但一旦完成,它可以被廣泛使用而不必擔心錯誤。

這在概念和設計上類似於將foreach循環結構添加到傳統的命令式語言,這個想法是你不必自己編寫循環控件(因為它引入了一個錯誤的機會,增加了詳細程度以你對每個條目所做的方式而不是顯示你如何獲得每個條目。高級函數只允許你將結構的遍歷與在語言本身內可擴展遍歷時的操作分開。

應該注意的是,c#中的委托已經基本上被lambdas取代了,因為如果需要,編譯器可以簡單地將其視為一個不那么詳細的委托,但是也可以自由地將lambda表示的表達式傳遞給它傳遞給的函數以允許它(通常很復雜)通過Linq-to-Sql重構或重新定位欲望到其他領域,如數據庫查詢。

.net委托模型相對於c樣式函數指針的一個主要好處是它們實際上是要調用的函數的元組(兩個數據)以及要在其上調用函數的可選對象。 這允許您傳遞具有更強大狀態的函數。 由於編譯器可以使用它來構造后面的類(1),因此實例化該類的新實例並將局部變量放入其中,從而允許閉包

(1)它不必總是這樣做,但現在這是一個實現細節

在你的例子中,你的恭維是一樣的,所以你真正需要的是字符串數組。

如果您想在Command模式中使用委托,請想象您有:

public static void ShakeHands()
{ ... }

public static void HowAreYou()
{ ... }

public static void FrenchKissing()
{ ... }

您可以使用相同的簽名替換方法,但可以使用不同的操作。 你選擇了太簡單的例子,我的建議是 - 去深度找一本C#書。

這是一個真實世界的例子。 我經常在包裝某種外部調用時使用委托。 例如,我們有一個舊的應用服務器(我希望它會消失),我們通過.Net遠程連接。 我將通過'safecall'函數在代理中調用app服務器,如下所示:

private delegate T AppServerDelegate<T>();

private T processAppServerRequest<T>(AppServerDelegate<T> delegate_) {
           try{
              return delegate_();
           }
           catch{
              //Do a bunch of standard error handling here which will be 
              //the same for all appserver  calls.
           }

        }

//Wrapped public call to AppServer
public int PostXYZRequest(string requestData1, string requestData2, 
   int pid, DateTime latestRequestTime){
           processAppServerRequest<int>(
              delegate {
                 return _appSvr.PostXYZRequest(
                    requestData1, 
                    requestData2, 
                    pid, 
                    latestRequestTime);  
              });

顯然,錯誤處理比這更好,但你得到了粗略的想法。

委托用於“調用”其他類中的代碼(可能不一定在同一個,類,或.cs或甚至相同的程序集中)。

在您的示例中,代理可以簡單地替換為您指出的if語句。

但是,代理指向代碼中某處“生活”的函數,這些函數出於組織原因(例如,您無法訪問)(輕松)。

代理和相關的語法糖已經顯着改變了C#世界(2.0+)代表是類型安全的函數指針 - 因此您可以在未來的任何地方使用委托來調用/執行代碼塊

我能想到的廣泛的部分

回調/事件處理程序 :在EventX發生時執行此操作。 或者在准備好我的異步方法調用的結果時執行此操作。

myButton.Click += delegate { Console.WriteLine("Robbery in progress. Call the cops!"); }

LINQ :元素的選擇,投影等,您希望在將每個元素傳遞到管道之前對其執行某些操作。 例如,選擇所有偶數的數字,然后返回每個數字的平方

var list = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }
                 .Where(delegate(int x) { return ((x % 2) == 0); })
                 .Select(delegate(int x) { return x * x; });    
// results in 4, 16, 36, 64, 100

我覺得很有用的另一個用途是,如果我希望執行相同的操作,傳遞相同的數據或在同一對象類型的多個實例中觸發相同的操作。

在.NET中,從后台線程更新UI時也需要委托。 由於您無法從與創建控件的線程不同的線程更新控件,因此您需要使用創建線程的上下文調用更新代碼(主要使用this.Invoke)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM