柚子快報(bào)激活碼778899分享:開發(fā)語言 C# 委托
柚子快報(bào)激活碼778899分享:開發(fā)語言 C# 委托
文章目錄
1. Delegate 委托委托 vs 接口事件模式 / 委托模式委托組合委托中的協(xié)變和逆變
2. Func & Action 委托對Func和Action泛型委托使用變體Func 與 Action 中異常處理
3. 其他委托:Predicate & Comparison & Converter
1. Delegate 委托
在C#中,委托是一種引用類型,表示對具有特定參數(shù)列表和返回類型的方法的引用,即委托就是一種用來指向一個(gè)方法的類型變量,可以通過委托實(shí)例調(diào)用方法,關(guān)鍵字是 delegate。
// 聲明一個(gè)委托
public delegate void MyDelegate(string message);
public class Program
{
static void Main(string[] args)
{
// 創(chuàng)建委托實(shí)例,綁定到具體的方法
MyDelegate myDelegate = new MyDelegate(DisplayMessage);
// 使用委托
myDelegate("Hello, World!");
}
// 與委托具有相同簽名的方法
static void DisplayMessage(string message)
{
Console.WriteLine(message);
}
}
現(xiàn)在我們可以更簡單的方式使用委托:
public class Program
{
static void Main(string[] args)
{
MyDelegate myDelegate = message => Console.WriteLine(message);
myDelegate("Hello, World!");
}
}
委托 vs 接口
委托和接口是不是感覺很類似?都是分離類型的聲明和實(shí)現(xiàn),且都可以由不了解實(shí)現(xiàn)該接口或委托的類對象使用,那么這兩個(gè)使用場景有什么區(qū)別呢?
C# 官方給出的建議, 以下情況請使用委托:(后面 事件模式/委托模式 舉例說明)
當(dāng)使用事件設(shè)計(jì)模式時(shí)當(dāng)封裝靜態(tài)方法可取時(shí)當(dāng)調(diào)用方不需要訪問實(shí)現(xiàn)該方法的對象中的其他屬性、方法或接口時(shí)需要方便的組合當(dāng)類可能需要該方法的多個(gè)實(shí)現(xiàn)時(shí)
以下情況請使用接口:
當(dāng)存在一組可能被調(diào)用的相關(guān)方法時(shí)當(dāng)類只需要方法的單個(gè)實(shí)現(xiàn)時(shí)當(dāng)使用接口的類想要將該接口強(qiáng)制轉(zhuǎn)換為其它接口或類類型時(shí)當(dāng)正在實(shí)現(xiàn)的方法鏈接到類的類型或標(biāo)識(shí)時(shí),例如比較方法
事件模式 / 委托模式
也可能翻譯有偏差,并沒有搜索到事件設(shè)計(jì)模式(eventing design pattern)。我們說的委托模式,是否可以認(rèn)為是這里指的事件設(shè)計(jì)模式? 委托模式解耦了委托者與被委托者,同時(shí)又需要委托者與被委托者協(xié)同完成同一個(gè)事件。
如下代碼示例,#1 事件模式時(shí),委托者即事件發(fā)布類(publisher),被委托者即訂閱類(subscriber),同時(shí)由于**#5 訂閱類的多樣性**,對于同一事件會(huì)有不同的訂閱處理,且**#3 委托者只需要發(fā)布事件,而不需要關(guān)心訂閱者任何其他細(xì)節(jié)**:
interface ISubscriber
{
// 事件處理程序
public void HandleSimpleEvent(object sender, EventArgs e);
}
public abstract class BaseSubscriber : ISubscriber
{
public void Subscribe(SimplePublisher publisher)
{
publisher.SimpleEvent += HandleSimpleEvent;
}
public abstract void HandleSimpleEvent(object sender, EventArgs e);
}
// 訂閱事件類1
public class SimpleSubscriber : BaseSubscriber
{
public override void HandleSimpleEvent(object sender, EventArgs e)
{
Console.WriteLine("SimpleEvent is raised.");
}
}
// 訂閱事件類2
public class ComplexSubscriber : BaseSubscriber
{
public override void HandleSimpleEvent(object sender, EventArgs e)
{
Console.WriteLine("Do complex work in ComplexSubscriber.");
Console.WriteLine("ComplexEvent is raised.");
}
}
public class SimplePublisher
{
// 定義委托類型
public delegate void EventHandler(object sender, EventArgs e);
// 定義事件
public event EventHandler SimpleEvent;
// 觸發(fā)事件的方法
public void RaiseEvent()
{
// 事件可能為null,所以先檢查
EventHandler handler = SimpleEvent;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
}
// 使用示例
public class Program
{
public static void Main()
{
var subscribers = GetAllSubscribers();
if (!subscribers.Any())
{
Console.WriteLine("No subscriber found!");
return;
}
SimplePublisher publisher = new SimplePublisher();
foreach (var subscriber in subscribers)
{
subscriber.Subscribe(publisher);
// 觸發(fā)事件
publisher.RaiseEvent();
}
}
// 通過反射獲取所有訂閱者
private static List
{
var pluginTypes = Assembly.GetExecutingAssembly().GetTypes()
.Where(t => t.IsClass && !t.IsAbstract && typeof(BaseSubscriber).IsAssignableFrom(t));
List
foreach (var pluginType in pluginTypes)
{
var instance = Activator.CreateInstance(pluginType) as BaseSubscriber;
if (instance != null)
{
subscribers.Add(instance);
}
}
return subscribers;
}
}
}
------------------Output-----------------------
Do complex work in ComplexSubscriber.
ComplexEvent is raised.
SimpleEvent is raised.
委托組合
c# 允許我們用 + 將多個(gè)委托組合成一個(gè)新的委托,組合的委托可以調(diào)用組成它的所有委托,但是注意,只有相同類型的委托才可以組合。運(yùn)算符 - 可以從組合的委托中移除某個(gè)委托。
delegate void Del(string s);
class TestClass
{
static void Hello(string s)
{
System.Console.WriteLine(" Hello, {0}!", s);
}
static void Goodbye(string s)
{
System.Console.WriteLine(" Goodbye, {0}!", s);
}
static void Main()
{
Del a, b, c, d;
// Create the delegate object a that references
// the method Hello:
a = Hello;
// Create the delegate object b that references
// the method Goodbye:
b = Goodbye;
// The two delegates, a and b, are composed to form c:
c = a + b;
// Remove a from the composed delegate, leaving d,
// which calls only the method Goodbye:
d = c - a;
System.Console.WriteLine("Invoking delegate a:");
a("A");
System.Console.WriteLine("Invoking delegate b:");
b("B");
System.Console.WriteLine("Invoking delegate c:");
c("C");
System.Console.WriteLine("Invoking delegate d:");
d("D");
}
}
/* Output:
Invoking delegate a:
Hello, A!
Invoking delegate b:
Goodbye, B!
Invoking delegate c:
Hello, C!
Goodbye, C!
Invoking delegate d:
Goodbye, D!
*/
委托中的協(xié)變和逆變
協(xié)變:下面官方示例種 Dogs 集成 Mammals,所以 DogsHandler 可以分配給 HandlerMethod 委托。
class Mammals {}
class Dogs : Mammals {}
class Program
{
// Define the delegate.
public delegate Mammals HandlerMethod();
public static Mammals MammalsHandler()
{
return null;
}
public static Dogs DogsHandler()
{
return null;
}
static void Test()
{
HandlerMethod handlerMammals = MammalsHandler;
// Covariance enables this assignment.
HandlerMethod handlerDogs = DogsHandler;
}
}
逆變:逆變可以使用一個(gè)事件處理程序而不是多個(gè)單獨(dú)的處理程序,如下 MultiHandler 中的第二個(gè)參數(shù) System.EventArgs 是 KeyEventhandler 和 MouseEventHandler 的基類,因此這兩個(gè)委托都可以作為MultiHandler 入?yún)ⅰ?/p>
public delegate void KeyEventHandler(object sender, KeyEventArgs e);
public delegate void MouseEventHandler(object sender, MouseEventArgs e);
// Event handler that accepts a parameter of the EventArgs type.
private void MultiHandler(object sender, System.EventArgs e)
{
label1.Text = System.DateTime.Now.ToString();
}
public Form1()
{
InitializeComponent();
// You can use a method that has an EventArgs parameter,
// although the event expects the KeyEventArgs parameter.
this.button1.KeyDown += this.MultiHandler;
// You can use the same method
// for an event that expects the MouseEventArgs parameter.
this.button1.MouseClick += this.MultiHandler;
}
2. Func & Action 委托
Func 是接受任意數(shù)量的參數(shù)并且有返回值的委托.
public delegate TResult Func
// Func 的 TResult前可以定義任意多個(gè)入?yún)?T1, T2 ... ...
public delegate TResult Func
Action 是接受任意數(shù)量的參數(shù)且沒有返回值的委托。
public delegate void Action
對Func和Action泛型委托使用變體
這里給出官方示例,使用具有協(xié)變類型參數(shù)的委托:當(dāng)一個(gè)方法的返回值類型 TResult 繼承了某個(gè)類A,那么就可以將這個(gè)方法分配給 Func
// Simple hierarchy of classes.
public class Person { }
public class Employee : Person { }
class Program
{
static Employee FindByTitle(String title)
{
return new Employee();
}
static void Test()
{
Func
Func
findPerson = findEmployee;
}
}
使用具有逆變類型參數(shù)的委托
public class Person { }
public class Employee : Person { }
class Program
{
static void AddToContacts(Person person)
{
// do something
}
static void Test()
{
Action
Action
addEmployeeToContacts = addPersonToContacts;
}
}
Func 與 Action 中異常處理
在業(yè)務(wù)實(shí)現(xiàn)的時(shí)候,很多場景需要保證數(shù)據(jù)的原子性,例如帶有數(shù)據(jù)庫操作的、一系列動(dòng)作組成的場景,一旦中間某個(gè)動(dòng)作失敗,我們需要將所有狀態(tài)恢復(fù)。
假如這種場景選擇使用委托模式,并且在委托方法中拋出異常,由于委托者并不知道被委托者實(shí)際的代碼流程,即使出現(xiàn)異常,委托者也無法確定要回滾的內(nèi)容。
如一個(gè)簡單下單場景:
用戶下訂單庫存有效并預(yù)約庫存創(chuàng)建訂單調(diào)用支付進(jìn)行支付實(shí)際扣減庫存訂單生效狀態(tài)更新
假使拋出了一個(gè)非常精確的異常,例如訂單在扣減庫存階段失敗,并需要委托者處理,那么委托者就必然需要了解被委托者內(nèi)部實(shí)現(xiàn)細(xì)節(jié),也就違背了我們使用委托模式的初衷。
所以我們應(yīng)避免在 Func 和 Action 中拋出異常,或者說對于有原子性要求的業(yè)務(wù),應(yīng)避免使用委托模式。如果場景貼合委托模式,卻沒有辦法保證相關(guān)的表達(dá)式絕對不拋出異常,那么必須采用其他開銷更大的防護(hù)措施,即把異常情況頁考慮進(jìn)來。例如:
拷貝源數(shù)據(jù),并在源數(shù)據(jù)上進(jìn)行操作,整個(gè)委托執(zhí)行成功時(shí),才會(huì)真正覆蓋源數(shù)據(jù)。添加異常處理邏輯和額外的數(shù)據(jù)恢復(fù)流程,例如可以設(shè)計(jì)整個(gè)流程能多次重復(fù)執(zhí)行,每個(gè)步驟的中間結(jié)果都保存下來,通過不同的flag來判斷當(dāng)前流程是否執(zhí)行成功,如果成功則跳過直接執(zhí)行下一個(gè)步驟,如果不成功則嘗試重試。對于始終無法成功的場景,采用其他流程用來恢復(fù)數(shù)據(jù)(可能就需要人工參與)。
3. 其他委托:Predicate & Comparison & Converter
Predicate 是用來判斷某個(gè)條件是否成立的布爾委托。
public delegate bool Predicate
Comparison 是比較兩個(gè)同類型對象的委托。
public delegate int Comparison
Converter
public delegate TOutput Converter
柚子快報(bào)激活碼778899分享:開發(fā)語言 C# 委托
參考閱讀
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。