Earn More by Sharing What You Love
Do you create content for developers working with .NET, C#, Java, Python, or Node.js? Turn your expertise into extra income!

Tim Corey
1h 9m 11s
C#中的委派是一個強大的功能,但許多開發者不熟悉如何有效使用。 Tim Corey 在 "Delegates in C# - A practical demonstration, including Action and Func" 的影片中,提供了委派是什麼、如何使用它們以及它們為什麼有用的詳細說明。
這篇文章將提供 Tim 關於 C#中的委派的專業見解,為您清楚說明其使用及實際應用。 您將了解委派如何提升您的程式程式碼的靈活性和效率,並通過如在購物車系統中的使用範例。
Tim 介紹了委派的概念,強調其在 C# 中的力量和多用途性。 他向觀眾保證,儘管有些術語讓人畏懼,但委派的基礎是簡單的。 Tim 的目標是破解委派的奧秘,並涵蓋如 func 和 action 的特殊型別。
Tim 設置了一個演示應用程式以說明委派的使用。 解決方案包含三個專案:一個控制台 UI,Demo Library,和一個 WinForm UI。 最初的重點在於控制台 UI 和 Demo Library。
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleUI
{
class Program
{
static ShoppingCartModel cart = new ShoppingCartModel();
static void Main(string[] args)
{
PopulateCartWithDemoData();
Console.WriteLine($"The total for the cart is {cart.GenerateTotal():C2}");
Console.ReadLine();
}
private static void PopulateCartWithDemoData()
{
cart.Items.Add(new ProductModel { ItemName = "Cereal", Price = 3.63M });
cart.Items.Add(new ProductModel { ItemName = "Milk", Price = 2.95M });
cart.Items.Add(new ProductModel { ItemName = "Strawberries", Price = 7.51M });
cart.Items.Add(new ProductModel { ItemName = "Blueberries", Price = 6.75M });
}
}
}
public class ShoppingCartModel
{
public List<ProductModel> Items { get; set; } = new List<ProductModel>();
public decimal GenerateTotal()
{
decimal subtotal = Items.Sum(x => x.Price);
if (subtotal > 100)
{
return subtotal * 0.80M;
}
else if (subtotal > 50)
{
return subtotal * 0.85M;
}
else if (subtotal > 10)
{
return subtotal * 0.90M;
}
else
{
return subtotal;
}
}
}
public class ProductModel
{
public string ItemName { get; set; }
public decimal Price { get; set; }
}Imports System
Imports System.Collections.Generic
Imports System.Linq
Namespace ConsoleUI
Class Program
Private Shared cart As New ShoppingCartModel()
Shared Sub Main(args As String())
PopulateCartWithDemoData()
Console.WriteLine($"The total for the cart is {cart.GenerateTotal():C2}")
Console.ReadLine()
End Sub
Private Shared Sub PopulateCartWithDemoData()
cart.Items.Add(New ProductModel With {.ItemName = "Cereal", .Price = 3.63D})
cart.Items.Add(New ProductModel With {.ItemName = "Milk", .Price = 2.95D})
cart.Items.Add(New ProductModel With {.ItemName = "Strawberries", .Price = 7.51D})
cart.Items.Add(New ProductModel With {.ItemName = "Blueberries", .Price = 6.75D})
End Sub
End Class
End Namespace
Public Class ShoppingCartModel
Public Property Items As List(Of ProductModel) = New List(Of ProductModel)()
Public Function GenerateTotal() As Decimal
Dim subtotal As Decimal = Items.Sum(Function(x) x.Price)
If subtotal > 100 Then
Return subtotal * 0.80D
ElseIf subtotal > 50 Then
Return subtotal * 0.85D
ElseIf subtotal > 10 Then
Return subtotal * 0.90D
Else
Return subtotal
End If
End Function
End Class
Public Class ProductModel
Public Property ItemName As String
Public Property Price As Decimal
End ClassTim 說明了演示應用程式的結構和功能:
購物車模型:表示擁有商品列表 (ProductModel) 的購物車,並根據小計計算包含折扣的總成本。
產品模型:代表具有名稱和價格屬性的單個物品。
控制台應用程式:用演示資料填充購物車,計算總數並顯示。
Tim 逐步講解 GenerateTotal 方法中的折扣邏輯,說明小計如何確定應用的折扣:
Tim 使用斷點來演示計算和折扣邏輯,確保觀眾在介紹委派之前理解基礎。
在本節中,Tim Corey 深入探討 C# 中的委派概念,說明它們如何運行,並通過實際的程式碼範例演示其使用。
Tim 解釋了委派基本上是將方法作為參數傳遞的一種方式。 與其傳遞變數或屬性,您可以傳遞方法,這允許更靈活和可重用的程式碼。
Tim 這樣解析建立和使用委派的過程:
委派定義在類的頂部,指定返回型別和參數型別。
public delegate void MentionDiscount(decimal subtotal);Public Delegate Sub MentionDiscount(subtotal As Decimal)委派作為參數在 ShoppingCartModel 類的 GenerateTotal 方法中使用。
public decimal GenerateTotal(MentionDiscount mentionDiscount)
{
decimal subtotal = Items.Sum(x => x.Price);
// Call a method passed as a delegate
mentionDiscount(subtotal);
if (subtotal > 100)
{
return subtotal * 0.80M;
}
else if (subtotal > 50)
{
return subtotal * 0.85M;
}
else if (subtotal > 10)
{
return subtotal * 0.90M;
}
else
{
return subtotal;
}
}
在 Program 類中建立一個符合委派簽名的方法。
private static void SubtotalAlert(decimal subtotal)
{
Console.WriteLine($"The subtotal is {subtotal:C2}");
}
該方法通過委派傳遞給 GenerateTotal 方法。
class Program
{
static ShoppingCartModel cart = new ShoppingCartModel();
static void Main(string[] args)
{
PopulateCartWithDemoData();
Console.WriteLine($"The total for the cart is {cart.GenerateTotal(SubtotalAlert):C2}");
Console.ReadLine();
}
private static void PopulateCartWithDemoData()
{
cart.Items.Add(new ProductModel { ItemName = "Cereal", Price = 3.63M });
cart.Items.Add(new ProductModel { ItemName = "Milk", Price = 2.95M });
cart.Items.Add(new ProductModel { ItemName = "Strawberries", Price = 7.51M });
cart.Items.Add(new ProductModel { ItemName = "Blueberries", Price = 6.75M });
}
}
Tim 運行應用程式以展示委派如何工作。 控制台輸出顯示購物車的小計和總計,表明 SubtotalAlert 方法已成功通過委派傳遞並執行。

然後,Tim Corey 探索了 C# 中 func 和 action 委派的使用。 這些是 Microsoft 提供的特殊型別委派,用於簡化與泛型的委派使用。
Tim 突出了一個常見問題:硬編碼的折扣邏輯在 GenerateTotal 方法中。 這種方法不靈活且需要程式碼更改、重新編譯和重新部署每當折扣規則改變時。
public decimal GenerateTotal()
{
decimal subtotal = Items.Sum(x => x.Price);
if (subtotal > 100)
{
return subtotal * 0.80M;
}
else if (subtotal > 50)
{
return subtotal * 0.85M;
}
else if (subtotal > 10)
{
return subtotal * 0.90M;
}
else
{
return subtotal;
}
}Public Function GenerateTotal() As Decimal
Dim subtotal As Decimal = Items.Sum(Function(x) x.Price)
If subtotal > 100 Then
Return subtotal * 0.80D
ElseIf subtotal > 50 Then
Return subtotal * 0.85D
ElseIf subtotal > 10 Then
Return subtotal * 0.90D
Else
Return subtotal
End If
End FunctionTim 介紹了 func 委派以解決硬編碼的折扣問題。 Func 委派是一種通用的委派,表示具有返回型別和最多16個輸入參數的方法簽名。
在 GenerateTotal 方法中使用 func 委派以動態處理折扣計算。
public decimal GenerateTotal(Func<List<ProductModel>, decimal, decimal> calculateDiscountedTotal)
{
decimal subtotal = Items.Sum(x => x.Price);
MentionDiscount(subtotal);
return calculateDiscountedTotal(Items, subtotal);
}
在 Program 類中建立一個符合 func 委派簽名的方法。
private static decimal CalculateLevelDiscount(List<ProductModel> items, decimal subtotal)
{
if (subtotal > 100)
{
return subtotal * 0.80M;
}
else if (subtotal > 50)
{
return subtotal * 0.85M;
}
else if (subtotal > 10)
{
return subtotal * 0.90M;
}
else
{
return subtotal;
}
}
CalculateLevelDiscount 方法通過 func 委派傳遞給 GenerateTotal 方法。
class Program
{
static ShoppingCartModel cart = new ShoppingCartModel();
static void Main(string[] args)
{
PopulateCartWithDemoData();
Console.WriteLine($"The total for the cart is {cart.GenerateTotal(CalculateLevelDiscount):C2}");
Console.ReadLine();
}
private static void PopulateCartWithDemoData()
{
cart.Items.Add(new ProductModel { ItemName = "Cereal", Price = 3.63M });
cart.Items.Add(new ProductModel { ItemName = "Milk", Price = 2.95M });
cart.Items.Add(new ProductModel { ItemName = "Strawberries", Price = 7.51M });
cart.Items.Add(new ProductModel { ItemName = "Blueberries", Price = 6.75M });
}
private static decimal CalculateLevelDiscount(List<ProductModel> items, decimal subtotal)
{
if (subtotal > 100)
{
return subtotal * 0.80M;
}
else if (subtotal > 50)
{
return subtotal * 0.85M;
}
else if (subtotal > 10)
{
return subtotal * 0.90M;
}
else
{
return subtotal;
}
}
}
Tim 演示了修改後的應用程式,顯示其正確運行並根據提供的邏輯動態計算折扣。

Tim 比較了自定義委派和 func 委派:
委派:需要明確定義的簽名,提供清晰的文件和結構。
Func:更簡潔但每次需要指定輸入和輸出型別,這可能不明確。
兩種方法都提供了靈活性,但選擇取決於特定的使用案例和應用程式的複雜性。
Tim Corey 解釋了關於委派使用的常見問題:如果所有工作似乎都在其他地方完成,為什麼要有委派?
Tim 解釋說,委派的目的是為程式碼提供靈活性和可擴展性。 ShoppingCartModel 類中的 GenerateTotal 方法可能不僅僅是計算折扣。 它可能還處理例如檢查庫存可用性、驗證購物車內容或其他商業邏輯的任務。 委派允許您為特殊任務或自定義行為傳遞特定方法而不改變核心方法。 這使得程式碼更具模塊化且更易於維護。
委派尤其在以下情況下有用:
Tim 介紹了 Action 委派,這是 C# 中的另一種特殊型別的委派。 Action 委派類似於 Func,但它不返回值(即返回 void)。
在 GenerateTotal 方法中定義 Action 委派以處理警報或消息。
public decimal GenerateTotal(Func<List<ProductModel>, decimal, decimal> calculateDiscountedTotal, Action<string> tellUserWeAreDiscounting)
{
decimal subtotal = Items.Sum(x => x.Price);
MentionSubtotal(subtotal);
tellUserWeAreDiscounting("We are applying your discount.");
return calculateDiscountedTotal(Items, subtotal);
}
定義一個方法以符合 Action 委派的簽名。
private static void AlertUser(string message)
{
Console.WriteLine(message);
}
通過 Action 委派將 AlertUser 方法傳遞給 GenerateTotal 方法。
class Program
{
static ShoppingCartModel cart = new ShoppingCartModel();
static void Main(string[] args)
{
PopulateCartWithDemoData();
Console.WriteLine($"The total for the cart is {cart.GenerateTotal(CalculateLevelDiscount, AlertUser):C2}");
Console.ReadLine();
}
private static void PopulateCartWithDemoData()
{
cart.Items.Add(new ProductModel { ItemName = "Cereal", Price = 3.63M });
cart.Items.Add(new ProductModel { ItemName = "Milk", Price = 2.95M });
cart.Items.Add(new ProductModel { ItemName = "Strawberries", Price = 7.51M });
cart.Items.Add(new ProductModel { ItemName = "Blueberries", Price = 6.75M });
}
private static decimal CalculateLevelDiscount(List<ProductModel> items, decimal subtotal)
{
if (subtotal > 100)
{
return subtotal * 0.80M;
}
else if (subtotal > 50)
{
return subtotal * 0.85M;
}
else if (subtotal > 10)
{
return subtotal * 0.90M;
}
else
{
return subtotal;
}
}
private static void AlertUser(string message)
{
Console.WriteLine(message);
}
}
Tim 展示了如何使用匿名方法,允許您在不命名它們的情況下即時定義方法。
您可以在需要的地方直接定義方法,而不是建立命名方法。
class Program
{
static ShoppingCartModel cart = new ShoppingCartModel();
static void Main(string[] args)
{
PopulateCartWithDemoData();
Console.WriteLine($"The total for the cart is {cart.GenerateTotal((items, subtotal) =>
{
if (subtotal > 100) return subtotal * 0.80M;
else if (subtotal > 50) return subtotal * 0.85M;
else if (subtotal > 10) return subtotal * 0.90M;
else return subtotal;
},
(message) => Console.WriteLine(message)):C2}");
Console.ReadLine();
}
private static void PopulateCartWithDemoData()
{
cart.Items.Add(new ProductModel { ItemName = "Cereal", Price = 3.63M });
cart.Items.Add(new ProductModel { ItemName = "Milk", Price = 2.95M });
cart.Items.Add(new ProductModel { ItemName = "Strawberries", Price = 7.51M });
cart.Items.Add(new ProductModel { ItemName = "Blueberries", Price = 6.75M });
}
}
=> 運算符(lambda 表達式)來直接內聯定義方法體。通過使用委派,包括 Func、Action 和匿名方法,開發者可以建立更動態和模塊化的程式碼,允許靈活和可重用的組件。
在此部分中,Tim Corey 展示了委派的力量,通過將其使用擴展到 WinForms 應用程式。 這突顯了委派如何促進在不同使用者介面(UI)背景中的不同行為。
該表單有兩個按鈕:一個用於演示訊息框,另一個用於演示文字框。
也包括了一個 ShoppingCartModel 和一個用來填充範例資料的方法。
public partial class Dashboard : Form
{
ShoppingCartModel cart = new ShoppingCartModel();
public Dashboard()
{
InitializeComponent();
PopulateCartWithDemoData();
}
private void PopulateCartWithDemoData()
{
cart.Items.Add(new ProductModel { ItemName = "Cereal", Price = 3.63M });
cart.Items.Add(new ProductModel { ItemName = "Milk", Price = 2.95M });
cart.Items.Add(new ProductModel { ItemName = "Strawberries", Price = 7.51M });
cart.Items.Add(new ProductModel { ItemName = "Blueberries", Price = 6.75M });
}
private void messageBoxDemoButton_Click(object sender, EventArgs e)
{
decimal total = cart.GenerateTotal(SubtotalAlert, CalculateLevelDiscount, PrintOutDiscountAlert);
MessageBox.Show($"The total is {total:C2}");
}
private void textBoxDemoButton_Click(object sender, EventArgs e)
{
// Code for TextBox demo will go here
}
}Public Partial Class Dashboard
Inherits Form
Private cart As New ShoppingCartModel()
Public Sub New()
InitializeComponent()
PopulateCartWithDemoData()
End Sub
Private Sub PopulateCartWithDemoData()
cart.Items.Add(New ProductModel With {.ItemName = "Cereal", .Price = 3.63D})
cart.Items.Add(New ProductModel With {.ItemName = "Milk", .Price = 2.95D})
cart.Items.Add(New ProductModel With {.ItemName = "Strawberries", .Price = 7.51D})
cart.Items.Add(New ProductModel With {.ItemName = "Blueberries", .Price = 6.75D})
End Sub
Private Sub messageBoxDemoButton_Click(sender As Object, e As EventArgs) Handles messageBoxDemoButton.Click
Dim total As Decimal = cart.GenerateTotal(AddressOf SubtotalAlert, AddressOf CalculateLevelDiscount, AddressOf PrintOutDiscountAlert)
MessageBox.Show($"The total is {total:C2}")
End Sub
Private Sub textBoxDemoButton_Click(sender As Object, e As EventArgs) Handles textBoxDemoButton.Click
' Code for TextBox demo will go here
End Sub
End Class此方法將用於顯示含折扣資訊的警報。
private void PrintOutDiscountAlert(string message)
{
MessageBox.Show(message);
}
此方法將在訊息框中顯示小計。
private void SubtotalAlert(decimal subtotal)
{
MessageBox.Show($"The subtotal is {subtotal:C2}");
}
此方法將根據購物車中的物品數量計算折扣。
private decimal CalculateLevelDiscount(List<ProductModel> items, decimal subtotal)
{
if (items.Count > 3)
{
return subtotal - 3M;
}
return subtotal - items.Count;
}
方法 messageBoxDemoButton_Click 演示了如何將委派傳遞給 GenerateTotal 方法並使用訊息框處理結果。
private void messageBoxDemoButton_Click(object sender, EventArgs e)
{
decimal total = cart.GenerateTotal(SubtotalAlert, CalculateLevelDiscount, PrintOutDiscountAlert);
MessageBox.Show($"The total is {total:C2}");
}
當按鈕被點擊時,WinForms 應用程式會顯示小計和總計,演示委派的靈活性。

Tim Corey 清楚地解釋了 C#中的委派,涵蓋了其基礎、高級應用和實際範例,如在購物車中使用委派。 他展示了委派如何實現靈活且可重用的程式碼,包括 Func、Action 和匿名方法。 觀看完整的影片以學習如何在您的專案中有效應用委派!
Do you create content for developers working with .NET, C#, Java, Python, or Node.js? Turn your expertise into extra income!
Join our newsletter, you’ll get exclusive access on article updates. We value your privacy