【C#】事件,
前言:CLR事件模式建立在委托的基礎上,委托說調用回調方法的一種類型安全的方式。
我個人覺得事件本質就是委托,所以把委托弄清楚,只要知道事件基本語法就會使用了(如果說到線程安全,我個人覺得這個應該和線程一起去討論),所以這篇只做一個簡單的時間介紹和寫一些我看到的或我用到的一些代碼。
EventHandler
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
上面是C#的源碼,超簡單有木有(還是委托)。
有兩個類型:
1.Object sender :事件源
2.TEventArgs e :泛型,這裡可以自定義事件所需要的額外參數。
既然知道基本的語法,我們來看看怎麼寫。
internal sealed class NewMailEventArgs : EventArgs {
private readonly String m_from, m_to, m_subject;
public NewMailEventArgs(String from, String to, String subject) {
m_from = from; m_to = to; m_subject = subject;
}
public String From { get { return m_from; } }
public String To { get { return m_to; } }
public String Subject { get { return m_subject; } }
}
View Code
這裡我們定義另一個類NewMailEventArgs,注意了 這裡繼承了EventArgs,所有的事件參數的類都需要繼承這個父類。這裡都是我們回調函數所要用的參數。
internal class MailManager
{
public event EventHandler<NewMailEventArgs> NewMail;
protected virtual void OnNewMail(NewMailEventArgs e)
{
if (NewMail!=null)
NewMail(this, e);
}
}
View Code
這裡定義了一個事件NewMail,EventHandler的參數類型是NewMailEventArgs(就是我們自定義的那個參數類),我們慣用的方式在同一個類裡面寫調用的方法,以On開頭+事件名結尾,但是這裡有一個很危險地方,就是多線程的時候,如果在執行的時候,其他地方正好取消了訂閱呢,又會變成NULL,所以這裡可以變成
internal class MailManager
{
public event EventHandler<NewMailEventArgs> NewMail;
protected virtual void OnNewMail(NewMailEventArgs e)
{
e.Raise(this, ref NewMail);
}
public void SimulateNewMail(string from, string to, string subject)
{
var e = new NewMailEventArgs(from, to, subject);
OnNewMail(e);
}
}
public static class EventArgExtensions
{
public static void Raise<TEventArgs>(this TEventArgs e, Object sender,
ref EventHandler<TEventArgs> eventDelegate) where TEventArgs : EventArgs
{
EventHandler<TEventArgs> temp = Interlocked.CompareExchange(ref eventDelegate, null, null);
if (temp != null) temp(sender, e);
}
}
View Code
這裡是我習慣用法,也是CLR書上推薦的用法,做一個通用Args的擴展類EventArgExtensions:主要說用於做線程安全和執行回調函數所用(我覺得這樣比較單一,封裝起來,要修改也只要修改一處就好,記住這裡是通用的事件調用,所以如果有特殊的需求,請不要加進來,可以在特殊的地方處理),有關Interlocked.CompareExchange可以看下官方的文檔,這個就是用來做輕量級線程同步比較的。
static void Main(string[] args)
{
var mail = new MailManager();
mail.NewMail += mail_NewMail;
mail.SimulateNewMail("a", "b", "c");
mail.NewMail -= mail_NewMail;
Console.ReadKey();
}
static void mail_NewMail(object sender, NewMailEventArgs e)
{
Console.WriteLine(e.From);
Console.WriteLine(e.To);
Console.WriteLine(e.Subject);
}
View Code
最後我們在Main方法中訂閱事件(mail.NewMail+=mail_NewMail;這裡的話直接+=然後按兩個Tab鍵,自己就出來了),取消訂閱用-=。是不是很簡單?到這裡基本的事件就已經說完了。
接下來分析一下CLR最後提供一份EventSet代碼(這份代碼也是我的大愛,可以集中管理起來事件,不會讓事件到處亂飛,喜歡的朋友可以研究下,這裡就不多做介紹了,提供的代碼還是有不少錯誤,比如空指針,沒有判斷是否存在key之類的情況,不過裡面的想法的確值得好好學習)
public sealed class EventKey : Object {
}
///////////////////////////////////////////////////////////////////////////////
public sealed class EventSet {
// The private dictionary used to maintain EventKey -> Delegate mappings
private readonly Dictionary<EventKey, Delegate> m_events =
new Dictionary<EventKey, Delegate>();
// Adds an EventKey -> Delegate mapping if it doesn't exist or
// combines a delegate to an existing EventKey
public void Add(EventKey eventKey, Delegate handler) {
Monitor.Enter(m_events);
Delegate d;
m_events.TryGetValue(eventKey, out d);
m_events[eventKey] = Delegate.Combine(d, handler);
Monitor.Exit(m_events);
}
// Removes a delegate from an EventKey (if it exists) and
// removes the EventKey -> Delegate mapping the last delegate is removed
public void Remove(EventKey eventKey, Delegate handler) {
Monitor.Enter(m_events);
// Call TryGetValue to ensure that an exception is not thrown if
// attempting to remove a delegate from an EventKey not in the set
Delegate d;
if (m_events.TryGetValue(eventKey, out d)) {
d = Delegate.Remove(d, handler);
// If a delegate remains, set the new head else remove the EventKey
if (d != null) m_events[eventKey] = d;
else m_events.Remove(eventKey);
}
Monitor.Exit(m_events);
}
// Raises the event for the indicated EventKey
public void Raise(EventKey eventKey, Object sender, EventArgs e) {
// Don't throw an exception if the EventKey is not in the set
Delegate d;
Monitor.Enter(m_events);
m_events.TryGetValue(eventKey, out d);
Monitor.Exit(m_events);
if (d != null) {
// Because the dictionary can contain several different delegate types,
// it is impossible to construct a type-safe call to the delegate at
// compile time. So, I call the System.Delegate type’s DynamicInvoke
// method, passing it the callback method’s parameters as an array of
// objects. Internally, DynamicInvoke will check the type safety of the
// parameters with the callback method being called and call the method.
// If there is a type mismatch, then DynamicInvoke will throw an exception.
d.DynamicInvoke(new Object[] { sender, e });
}
}
}
View Code
public class FooEventArgs : EventArgs { }
// Define the EventArgs-derived type for this event.
public class BarEventArgs : EventArgs { }
///////////////////////////////////////////////////////////////////////////////
internal class TypeWithLotsOfEvents {
// Define a private instance field that references a collection.
// The collection manages a set of Event/Delegate pairs.
// NOTE: The EventSet type is not part of the FCL, it is my own type.
private readonly EventSet m_eventSet = new EventSet();
// The protected property allows derived types access to the collection.
protected EventSet EventSet { get { return m_eventSet; } }
#region Code to support the Foo event (repeat this pattern for additional events)
// Define the members necessary for the Foo event.
// 2a. Construct a static, read-only object to identify this event.
// Each object has its own hash code for looking up this
// event’s delegate linked list in the object’s collection.
protected static readonly EventKey s_fooEventKey = new EventKey();
// 2d. Define the event’s accessor methods that add/remove the
// delegate from the collection.
public event EventHandler<FooEventArgs> Foo {
add { m_eventSet.Add(s_fooEventKey, value); }
remove { m_eventSet.Remove(s_fooEventKey, value); }
}
// 2e. Define the protected, virtual On method for this event.
protected virtual void OnFoo(FooEventArgs e) {
m_eventSet.Raise(s_fooEventKey, this, e);
}
// 2f. Define the method that translates input to this event.
public void SimulateFoo() {
OnFoo(new FooEventArgs());
}
#endregion
#region Code to support the Bar event
// 3. Define the members necessary for the Bar event.
// 3a. Construct a static, read-only object to identify this event.
// Each object has its own hash code for looking up this
// event’s delegate linked list in the object’s collection.
protected static readonly EventKey s_barEventKey = new EventKey();
// 3d. Define the event’s accessor methods that add/remove the
// delegate from the collection.
public event EventHandler<BarEventArgs> Bar {
add { m_eventSet.Add(s_barEventKey, value); }
remove { m_eventSet.Remove(s_barEventKey, value); }
}
// 3e. Define the protected, virtual On method for this event.
protected virtual void OnBar(BarEventArgs e) {
m_eventSet.Raise(s_barEventKey, this, e);
}
// 3f. Define the method that translates input to this event.
public void SimulateBar() {
OnBar(new BarEventArgs());
}
#endregion
}
View Code
關於Action和Func本質還是一樣的只是帶上了參數。
Action只有輸入參數,有好多重載的版本
Func有輸入參數,也有一個輸出參數,同樣有很多重載的版本
用一下就知道怎麼玩了,本質完全一樣的。
若有不對,不足之處請指出,請不要只寫一個:漏洞百出此類評價,謝謝大家的指點和幫助!
【C語言】有沒有一個可以清除 緩存中的按鍵 的函數
fflush(stdin)
清空標准輸入緩存
#include "stdio.h"
main()
{
char a,b;
scanf("%c",&a);
//fflush(stdin);
scanf("%c",&b);
printf("\n%c %c",a,b);
}
可以嘗試,沒有fflush(stdin)時,輸入一串字符"abcd",則a='a',b='b'
如果有fflush(stdin),則在輸入"abcd"後程序會繼續等待輸入,再輸入"efdfsd",結果a='a',b='e'
助【C語言】直接插入排序
已經測試過可行。
#include"stdio.h"
#define MAXSIZE 20//一個用作示例的小順序表的最大長度
int Insertsort(int r[],int n)
{//作直接插入排序
int i,j;
for(i=2;i<=n;i++)
{ r[0]=r[i];//r[0]用作哨兵單元
j=i-1;
while(r[0]<r[j])
{ r[j+1]=r[j];//記錄後移
j--;
}//while
r[j+1]=r[0];//插入到正確位置
for(j=1;j<=n;j++)//輸出每趟排序的結果
{
printf("%d ",r[j]);
}//for
printf("\n");
}//for
}//Insertsort
int main()
{
int n,i;//待排序的關鍵字個數
int r[MAXSIZE];
scanf("%d",&n);
for(i=1;i<=n;i++)//輸入待排序的關鍵字
scanf("%d",&r[i]);
Insertsort(r,n);
}