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 13s
C# 中的事件是許多開發者使用的重要概念,但在建立自己的事件方面,可能沒有完全理解。 Tim Corey 提供了一個全面的指南,講解如何建立和使用事件,探討在其影片"C# Events - Creating and Consuming Events in Your Application"中介紹的最佳實踐和進階功能。
在本文中,我們將探討 C# 事件,重點放在其語法、如何定義以及使用 Tim Corey 的影片例子來解決常見編程問題。 了解 C# 中的事件處理對構建響應式應用程式至關重要,而本文將幫助您掌握自定義錯誤日誌記錄、異常處理和事件驅動編程的核心概念。
在 C# 和編程語言中,事件在事件驅動編程中發揮著關鍵作用,使應用程式能夠對使用者交互或系統變更等操作做出響應。 與其他編程語言相比,C# 提供了結構化的事件處理方法,非常適合快速且小型的應用程式。 C# 中的事件由特定操作觸發,這些操作調用相應的事件處理程式來執行所需的任務。
Tim 首先解釋說,大多數開發者對 C# 中的事件很熟悉,但可能不知道如何建立自定義事件。 影片的目的是介紹事件,示範建立自定義事件的過程,討論其特性,並概述最佳實踐。
Tim 使用一個使用 Windows Forms (WinForms) 構建的簡單銀行應用程式來演示事件。 該應用程式模擬一個客戶的基本銀行操作,包括查看餘額和記錄交易。
Tim 解釋了演示銀行應用程式的後端程式碼:
對於程式碼範例,您可以直接參考 Tim Corey 的影片,他在 3:18 到 18:27 之間解釋程式碼。
在本節中,Tim Corey 深入研究 Windows Forms 中按鈕點擊事件的運作方式,說明這些事件如何連接並如何運行。
Tim 首先解釋了 Windows Forms 應用程式中大家熟知的按鈕點擊事件的概念。 當您在設計器中雙擊按鈕時,Visual Studio 會自動為點擊事件生成一個事件處理方法。
每當按鈕被點擊時,就會調用此方法。 這是您放置應響應按鈕點擊而執行的程式碼的地方。
private void recordTransactionButton_Click(object sender, EventArgs e)
{
// Code to handle the button click event
}
事件處理方法在窗體的設計文件 (FormName.Designer.cs) 中被連接到按鈕點擊事件。 這是通過使用 += 運算符將事件處理程式新增到按鈕的點擊事件來完成的。
this.recordTransactionButton.Click += new System.EventHandler(this.recordTransactionButton_Click);AddHandler Me.recordTransactionButton.Click, AddressOf Me.recordTransactionButton_ClickTim 解釋如何在 C# 中建立自定義事件,首先從 Account 類開始,這是事件將被觸發的地方。
使用 event 關鍵字定義事件。 它看起來類似於公共變數,但專門用於事件。
public event EventHandler<string> RaiseTransactionApprovedEvent;Public Event RaiseTransactionApprovedEvent As EventHandler(Of String)使用 Invoke 方法觸發事件,通常在定義事件的類內部。 Invoke 方法接受兩個參數:發送者(通常為 this)和事件資料(在此情況下為字串)。
private void OnTransactionApproved(string transactionName)
{
RaiseTransactionApprovedEvent?.Invoke(this, transactionName);
}
使用 += 運算符訂閱事件並定義當事件被觸發時的處理方法。
account.RaiseTransactionApprovedEvent += Account_RaiseTransactionApprovedEvent;
private void Account_RaiseTransactionApprovedEvent(object sender, string e)
{
// Code to handle the event
}
Tim 解釋如何使用事件在發生某些操作(例如交易被批准時)時自動更新瀏覽器 UI。
在交易被批准後觸發自定義事件。
public void AddDeposit(decimal amount, string depositName)
{
// Code to add deposit
RaiseTransactionApprovedEvent?.Invoke(this, depositName);
}
在主窗體中訂閱事件,以便每當一個新交易被批准時更新交易列表。
public MainForm()
{
InitializeComponent();
account.RaiseTransactionApprovedEvent += Account_RaiseTransactionApprovedEvent;
}
private void Account_RaiseTransactionApprovedEvent(object sender, string e)
{
// Update the UI with the new transaction
}
Tim Corey 解釋了當在 C# 中觸發事件時使用 ?.Invoke() 語法。 這種現代方法簡化了程式碼並確保了執行緒安全。
問號(?.)在 Invoke 前為空條件運算符。 它在調用之前先檢查事件處理程式是否為空,以防止潛在的例外。
以前,開發者使用多行程式碼檢查事件處理程式是否為空,然後調用它。
if (RaiseTransactionApprovedEvent != null)
{
RaiseTransactionApprovedEvent(this, depositName);
}
空條件運算符簡化了這一過程,在單行中進行空值檢查並調用事件。
RaiseTransactionApprovedEvent?.Invoke(this, depositName);RaiseTransactionApprovedEvent?.Invoke(Me, depositName)RaiseTransactionApprovedEvent 為空,則不會進行調用,有效避免任何例外。Tim 解釋如何在 Windows Forms 應用程式中監聽自定義事件並相應地更新 UI。
使用 += 運算符訂閱自定義事件。
customer.CheckingAccount.TransactionApprovedEvent += CheckingAccount_TransactionApprovedEvent;AddHandler customer.CheckingAccount.TransactionApprovedEvent, AddressOf CheckingAccount_TransactionApprovedEvent定義一個方法來處理事件。 此方法根據事件資料更新 UI。
private void CheckingAccount_TransactionApprovedEvent(object sender, string e)
{
// Update the UI with the new transaction
checkingTransactionsDataSource.DataSource = null;
checkingTransactionsDataSource.DataSource = customer.CheckingAccount.Transactions;
checkingBalanceLabel.Text = customer.CheckingAccount.Balance.ToString("C2");
}
Tim 示範了整個建立、觸發和處理自定義事件的過程。
在交易被批准或餘額變更後觸發事件。
private void OnTransactionApproved(string transactionName)
{
RaiseTransactionApprovedEvent?.Invoke(this, transactionName);
}
確保應用程式能夠監聽多個事件,例如交易和餘額變動,讓 UI 實時更新。
public MainForm()
{
InitializeComponent();
customer.CheckingAccount.TransactionApprovedEvent += CheckingAccount_TransactionApprovedEvent;
customer.SavingsAccount.TransactionApprovedEvent += SavingsAccount_TransactionApprovedEvent;
}
private void SavingsAccount_TransactionApprovedEvent(object sender, string e)
{
// Update the UI with the new savings transaction
savingsTransactionsDataSource.DataSource = null;
savingsTransactionsDataSource.DataSource = customer.SavingsAccount.Transactions;
savingsBalanceLabel.Text = customer.SavingsAccount.Balance.ToString("C2");
}
Tim 示範如何除錯事件並檢查隨其傳遞的資訊。
在事件處理方法中設置斷點,以檢查事件資料。
private void CheckingAccount_TransactionApprovedEvent(object sender, string e)
{
// Breakpoint here
}
Tim Corey 通過建立另一個自定義事件來處理透支情境,擴展了演示。 當透支發生時,該事件將被觸發並通知使用者透支金額。
Tim 首先在 Account 類中定義了一個新的針對透支情境的事件:
使用事件關鍵字定義事件,設為 decimal 型別以傳遞透支金額。
public event EventHandler<decimal> OverdraftEvent;Public Event OverdraftEvent As EventHandler(Of Decimal)在程式碼中成功發生透支的部分觸發事件。
if (overdraftSuccessful)
{
OverdraftEvent?.Invoke(this, overdraftAmount);
}
在儀表板窗體中訂閱透支事件。
customer.CheckingAccount.OverdraftEvent += CheckingAccount_OverdraftEvent;AddHandler customer.CheckingAccount.OverdraftEvent, AddressOf CheckingAccount_OverdraftEvent定義一個事件處理方法,在發生透支時顯示資訊。
private void CheckingAccount_OverdraftEvent(object sender, decimal e)
{
errorMessage.Text = $"You had an overdraft protection transfer of {e:C2}";
errorMessage.Visible = true;
}
當發生透支時,事件被觸發並更新 UI 以通知使用者。
You had an overdraft protection transfer of $20.44
Tim 解釋如何在多個語言和形式中監聽同一事件,顯示事件的靈活性。
向次級窗體新增標籤以顯示透支消息。
<asp:Label ID="errorMessage" runat="server" Visible="false" />
在次級窗體中訂閱透支事件。
customer.CheckingAccount.OverdraftEvent += SecondaryForm_OverdraftEvent;AddHandler customer.CheckingAccount.OverdraftEvent, AddressOf SecondaryForm_OverdraftEvent定義一個事件處理方法,以在次級窗體上顯示透支消息。
private void SecondaryForm_OverdraftEvent(object sender, decimal e)
{
errorMessage.Visible = true;
}
Tim 示範事件可以在多個窗體中同時處理,確保應用程式所有相關部分都正確響應事件。
Both the main form and the secondary form display the overdraft message when the event is triggered.
Tim 指出,清理事件監聽器以防止記憶體洩漏並確保應用程式性能正常的重要性。
在銷毀類執行個體或關閉窗體之前取消訂閱事件至關重要。
customer.CheckingAccount.OverdraftEvent -= CheckingAccount_OverdraftEvent;RemoveHandler customer.CheckingAccount.OverdraftEvent, AddressOf CheckingAccount_OverdraftEvent為什麼這很重要:
未能取消訂閱事件可能會導致記憶體洩漏,因為監聽事件的物件可能無法被正確垃圾回收。
避免使用匿名函式作為事件處理方法,因為這使得取消訂閱事件變得困難。
// Good practice: using named methods for event handlers' Good practice: using named methods for event handlersTim Corey 解釋了通過事件傳遞資料的最佳實踐,特別是使用類別而非簡單資料型別(如字串或 decimal)的好處。
Tim 先討論為什麼事件中使用簡單資料型別較少見,以及使用類別通常是更好的做法:
Tim 示範如何為透支事件建立自定義 EventArgs 類別。
建立一個繼承自 EventArgs 的新類別,並包含要通過事件傳遞的資料屬性。
public class OverdraftEventArgs : EventArgs
{
public decimal AmountOverdrafted { get; private set; }
public string MoreInfo { get; private set; }
public OverdraftEventArgs(decimal amountOverdrafted, string moreInfo)
{
AmountOverdrafted = amountOverdrafted;
MoreInfo = moreInfo;
}
}
更新事件聲明以使用自定義 EventArgs 類別。
public event EventHandler<OverdraftEventArgs> OverdraftEvent;Public Event OverdraftEvent As EventHandler(Of OverdraftEventArgs)在調用事件時傳遞自定義 EventArgs 類別的執行個體。
OverdraftEvent?.Invoke(this, new OverdraftEventArgs(amountNeeded, "Additional info"));OverdraftEvent?.Invoke(Me, New OverdraftEventArgs(amountNeeded, "Additional info"))Tim 強調在 EventArgs 類別中使用唯讀屬性以防止意外修改事件資料的重要性。
Tim 示範如何在事件處理程式中的一個方法中修改事件資料如果屬性不是唯讀的,可能會影響其他處理程式。
private void CheckingAccount_OverdraftEvent(object sender, OverdraftEventArgs e)
{
e.AmountOverdrafted = 1000; // This modification affects all handlers
}
使用私有設置器並通過構造函式傳遞資料以使屬性為唯讀。
public decimal AmountOverdrafted { get; private set; }
public string MoreInfo { get; private set; }
Tim Corey 突出了一個重要的例外,即使用事件資料屬性的私有設置器規則。 這個例外是當您需要允許事件監聽器修改事件資料時,如在基於某些條件可能取消交易的情況下。
Tim 提供了一個例子,其中一個事件處理程式可能需要取消交易。 這是通過在自定義 EventArgs 類別中新增一個 CancelTransaction 屬性來實現的。
向 EventArgs 類別新增具有 getter 和 setter 的公共屬性。
public class OverdraftEventArgs : EventArgs
{
public decimal AmountOverdrafted { get; private set; }
public string MoreInfo { get; private set; }
public bool CancelTransaction { get; set; } = false;
public OverdraftEventArgs(decimal amountOverdrafted, string moreInfo)
{
AmountOverdrafted = amountOverdrafted;
MoreInfo = moreInfo;
}
}
在儀表板中,事件處理程式可以設置此屬性以取消交易。
private void CheckingAccount_OverdraftEvent(object sender, OverdraftEventArgs e)
{
if (denyOverdraft.Checked)
{
e.CancelTransaction = true;
}
errorMessage.Text = $"You had an overdraft protection transfer of {e.AmountOverdrafted:C2}";
errorMessage.Visible = true;
}
在觸發事件的方法中,檢查 CancelTransaction 屬性是否設為 true,在繼續之前。
if (args.CancelTransaction)
{
return false; // Transaction is canceled
}
Tim 進一步完善應用程式,使其更具互動性和使用者友好性。
在表單中新增一個核取方塊,使使用者可以啟用或禁用透支保護。
private void InitializeComponent()
{
this.denyOverdraft = new System.Windows.Forms.CheckBox();
// Initialize other controls
this.denyOverdraft.Text = "Stop Overdrafts";
this.denyOverdraft.CheckedChanged += new System.EventHandler(this.denyOverdraft_CheckedChanged);
}
在核取方塊的事件處理方法中,更新邏輯以便在決定取消交易時考慮核取方塊狀態。
private void denyOverdraft_CheckedChanged(object sender, EventArgs e)
{
if (denyOverdraft.Checked)
{
// Logic to stop overdraft transactions
}
}
確保事件處理程式考慮到核取方塊狀態以允許或拒絕透支。
private void CheckingAccount_OverdraftEvent(object sender, OverdraftEventArgs e)
{
if (denyOverdraft.Checked)
{
e.CancelTransaction = true;
}
errorMessage.Text = $"You had an overdraft protection transfer of {e.AmountOverdrafted:C2}";
errorMessage.Visible = true;
}
Tim Corey 通過總結在 C# 中使用事件的關鍵點和最佳實踐來結束教程。
始終在銷毀物件之前移除事件監聽器,以防止記憶體洩漏。
使用 -= 運算符取消訂閱事件。
customer.CheckingAccount.OverdraftEvent -= CheckingAccount_OverdraftEvent;RemoveHandler customer.CheckingAccount.OverdraftEvent, AddressOf CheckingAccount_OverdraftEventTim Corey 的C# 事件綜合教程提供了有價值的見解和實用範例,幫助您有效地建立、處理和管理事件。 遵循這些最佳實踐,開發者可以建立更具互動性和響應的應用程式。
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