C#-Reihe von Artikelereignissen

PHPz
Freigeben: 2017-03-12 15:43:26
Original
1130 Leute haben es durchsucht

Inhalt der Datei:

  • EntwurfsoffenlegungEreignisTyp

  • Wie der Compiler Ereignisse implementiert

  • Art des Zuhörevents entwerfen

  • Das Event explizit umsetzen

Event: Definition Typen von Ereignismitgliedern ermöglichen es Typen, andere Objekte darüber zu benachrichtigen, dass bestimmte Dinge aufgetreten sind. Das

CLR-Ereignis--Modell basiert auf Delegaten, die eine Art sichere Möglichkeit zum Aufrufen von Rückrufmethoden darstellen, mit denen Objekte Benachrichtigungen erhalten, die sie abonnieren.

definiert die Typanforderungen für Ereignismitglieder, um die folgenden Funktionen bereitstellen zu können:

  1. Die Methode kann ihre Aufmerksamkeit für das Ereignis registrieren

  2. Die Methode kann ihre Aufmerksamkeit für das Ereignis aufheben

  3. Wenn das Ereignis eintritt, erhält die registrierte Methode eine Benachrichtigung

Dieser Artikel wird als E-Mail verschicktWenden Sie das -Programm als Beispiel an. Wenn eine E-Mail eintrifft, möchte der Benutzer, dass sie zur Verarbeitung an ein Faxgerät oder einen Pager weitergeleitet wird. Entwerfen Sie zunächst den Typ MainlManager, um eingehende E-Mails zu empfangen, wodurch das Ereignis NewMain verfügbar gemacht wird. Objekte anderer Art (Fax oder Pager) melden Interesse an der Veranstaltung. Dieses Ereignis wird ausgelöst, wenn MailManager eine neue E-Mail empfängt, wodurch die E-Mail an jedes registrierte Objekt verteilt wird, wobei jedes Objekt seine eigene Art hat, die E-Mail zu verarbeiten.

1.1 Entwerfen Sie den Typ des anzuzeigenden Ereignisses

Schritt 1: Definieren Sie den Typ, um alle zusätzlichen Informationen aufzunehmen, die an den Ereignisbenachrichtigungsempfänger gesendet werden müssen

Dies Der Typ enthält normalerweise eine Reihe privater Felder und einige schreibgeschützte öffentliche Eigenschaften , die diese Felder verfügbar machen.


 1 class NewMailEventArgs:EventArgs 2     { 3         private readonly string m_from, m_to, m_subject; 4         public NewMailEventArgs(string from,string to,string subject) 5         { 6             m_from = from; 7             m_to = to; 8             m_subject = subject; 9         }10         public string From { get { return m_from; } }11         public string To { get{ return m_to; } }12         public string Subject { get { return m_subject; } }13     }
Nach dem Login kopieren

Schritt 2: Ereignismitglieder definieren


class MailManager
    {        public event EventHandler<NewMailEventArgs> NewMail;
    }
Nach dem Login kopieren

wobei NewMail der Ereignisname ist. Der Ereignismitgliedstyp ist EventHandler und gibt an, dass alle Empfänger von Ereignisbenachrichtigungen eine Rückrufmethode bereitstellen müssen, deren Prototyp mit ihrem Delegatentyp übereinstimmt. Da der generische System.EventHandler-Delegatentyp wie folgt definiert ist:

public Delegate void EventHandler(Object sender,TEventArgs e);

Der Methodenprototyp muss also die folgende Form haben: void MethodName(Object sender,NewMailEventArgs e); Der Grund, warum der Ereignis--Modus erfordert, dass der Rückgabetyp aller Ereignisverarbeitung Programme ist void ist Da mehrere Rückrufmethoden aufgerufen werden können, nachdem ein Ereignis ausgelöst wurde, es jedoch keine Möglichkeit gibt, die Rückgabewerte aller Methoden zu erhalten, erlaubt die Rückgabe von void nicht, dass die Rückrufmethode einen Rückgabewert hat.

Schritt 3: Definieren Sie die Methode, die für das Auslösen des Ereignisses verantwortlich ist, um das registrierte Objekt über das Ereignis zu informieren


 1         /// <summary> 2         /// 定义负责引发事件的方法来通知事件的登记对象,该方法定义在MailManager中 3         /// 如果类是密封的,该方法要声明为私有和非虚 4         /// </summary> 5         /// <param name="e"></param> 6         protected virtual void OnNewMail(NewMailEventArgs e) 7         { 8             //出于线程安全考虑,现在将委托字段的引用复制到一个临时变量中 9             EventHandler<NewMailEventArgs> temp = Volatile.Read(ref NewMail);10             if(temp!=null)11             {12                 temp(this, e);13             }14         }
Nach dem Login kopieren
Die obige Methode verwendet das Volatile. Read()-Methode Um die Thread-Sicherheit zu gewährleisten, berücksichtigen Sie hauptsächlich die folgenden zwei Situationen:

1. Bestimmen Sie NewMail!=null direkt, aber bevor Sie NewMail aufrufen, entfernt ein anderer Thread möglicherweise einen Delegaten aus der Delegationskette, wodurch diese leer wird , was zu einer (NullReference

Exception)-Ausnahme führt.

2. Manche Leute können es auch in einer temporären Variablen speichern, ohne Volatile zu verwenden. Es ist jedoch theoretisch möglich, aber wenn der Compiler den Code optimiert und die temporäre Variable entfernt, ist es dasselbe wie im ersten Fall .

Die Verwendung von Volatile.Read zwingt NewMail zum Lesen, wenn dieser Aufruf erfolgt. Der Verweis muss in die temporäre Variable kopiert werden, was eine perfekte Lösung ist. Dies wird jedoch nicht in einem einzelnen Thread passieren.

Der vierte Schritt definiert die Methode zum Konvertieren der Eingabe in das erwartete Ereignis.


1 public void SimulateNewMail(string from,string to,string subject)2         {3             //构造一个对象来容纳想传给通知接收者的信息4             NewMailEventArgs e = new NewMailEventArgs(from, to, subject);5             //调用虚方法通知对象事件已反生6             //如果没有类型重写该方法7             //我们的对象将通知事件的所有登记对象8             OnNewMail(e);9         }
Nach dem Login kopieren
Die Methodenpunkte out Eine neue E-Mail ist im MailManager angekommen.

1.2 Wie der Compiler Ereignisse implementiert

In der MailManager-Klasse definieren wir das Ereignismitglied selbst in einem Satz: public event EventHandler

C# Der Compiler konvertiert es in den folgenden Code:


         //一个被初始化为null的私有字段
        private EventHandler<NewMailEventArgs> NewMail = null;        public void add_NewMail(EventHandler<NewMailEventArgs> value)
        {            //通过循环和对CompareExchange的调用,以一种线程安全的方式向事件添加委托            //CompareExchange是把目标操作数(第1参数所指向的内存中的数)            //与一个值(第3参数)比较,如果相等,            //则用另一个值(第2参数)与目标操作数(第1参数所指向的内存中的数)交换
            EventHandler<NewMailEventArgs> prevHandler;
            EventHandler<NewMailEventArgs> newMail = this.NewMail;            do
            {
                prevHandler = newMail;
                EventHandler<NewMailEventArgs> newHandler =
                    (EventHandler<NewMailEventArgs>)Delegate.Combine(prevHandler, value);
                newMail = Interlocked.CompareExchange<EventHandler<NewMailEventArgs>>(ref this.NewMail, newHandler, prevHandler);
            } while (newMail != prevHandler);
        }        public void remove_NewMail(EventHandler<NewMailEventArgs> value)
        {
            EventHandler<NewMailEventArgs> prevHandler;
            EventHandler<NewMailEventArgs> newMail = this.NewMail;            do
            {
                prevHandler = newMail;
                EventHandler<NewMailEventArgs> newHandler =
                    (EventHandler<NewMailEventArgs>)Delegate.Remove(prevHandler, value);
                newMail = Interlocked.CompareExchange<EventHandler<NewMailEventArgs>>(ref this.NewMail, newHandler, prevHandler);
            } while (newMail != prevHandler);
        }
Nach dem Login kopieren
In diesem Beispiel sind die Zugänglichkeit der Add- und Remove-Methode beide öffentlich, da das Ereignis NewMail deklariert ist als öffentlich. Die Zugänglichkeit eines Ereignisses bestimmt, welcher Code die Aufmerksamkeit für das Ereignis registrieren und die Registrierung aufheben kann. Auf das obige Delegate-Feld NewMail kann aber in jedem Fall nur der Typ selbst zugreifen. Zusätzlich zum oben generierten Code generiert der Compiler auch Ereignisdefinitionseinträge in den Metadaten der verwalteten Assembly. Enthält einige Flags und grundlegende Delegatentypen. Die CLR selbst verwendet diese Metadateninformationen nicht; die Laufzeit benötigt nur Zugriffsmethoden.

1.3 设计侦听事件的类型

如何定义一个类型来使用另一个类型提供的事件。以Fax类型为例:


internal class Fax
    {        public Fax(MailManager mm)
        {            //向MailManager的NewMail事件登记我们的回调方法
            mm.NewMail += FaxMsg;
        }        //新邮件到达,MailManager将调用这个方法        //sender表示MailManager对象,便于将信息回传给它        //e表示MailManager对象想传给我们的附加事件信息
        private void FaxMsg(object sender, NewMailEventArgs e)
        {
            Console.WriteLine("Fax 的消息from:{0} to:{1} subject:{2}", e.From, e.To, e.Subject);
        }        /// <summary>
        /// 注销        /// </summary>
        /// <param name="mm"></param>
        public void Unregister(MailManager mm)
        {
            mm.NewMail -= FaxMsg;
        }
    }
Nach dem Login kopieren

 电子邮件应用程序初始化时首先构造MailManager对象,并将对该对象的引用保存到变量中。然后构造Fax对象,并将MailManager对象引用作为实参传递。在Fax构造器中,使用+=登记对NewMail事件的关注。

1.4 显式实现事件

对于System.Windows.Forms.Control类型定义了大约70个事件。每个从Control派生类型创建对象都要浪费大量内存,而大多数我们只关心少数几个事件。如何通过显式实现事件来高效的实现提供了大量事件的类思路如下:

定义事件时:公开事件的每个对象都要维护一个集合(如字典)。集合将某种事件标识符作为健,将委托列表作为值。新对象构造时集合也是空白。登记对一个事件的关注会在集合中查找事件的标识符。如果事件标识符存在,新委托就和这个事件的委托列表合并,否则就添加事件标识符和委托。   

引发事件时:对象引发事件会在集合中查找事件的标识符,如果没有说明没有对象登记对这个事件的关注,所以也没委托需要回调。否则就调用与它关联的委托列表。


 1  public sealed class EventKey { } 2     public sealed class EventSet 3     { 4         //定义私有字典 5         private readonly Dictionary<EventKey, Delegate> m_events = 6             new Dictionary<EventKey, Delegate>(); 7         /// <summary> 8         /// 不存在添加,存在则和现有EventKey合并 9         /// </summary>       10         public void Add(EventKey eventKey,Delegate handler)11         {12             //确保操作唯一13             Monitor.Enter(m_events);14             Delegate d;15             //根据健获取值16             m_events.TryGetValue(eventKey, out d);17             //添加或合并18             m_events[eventKey] = Delegate.Combine(d, handler);19             Monitor.Exit(m_events);20         }21         /// <summary>22         /// 删除委托,在删除最后一个委托时还需删除字典中EventKey->Delegate23         /// </summary>       24         public void Remove(EventKey eventKey,Delegate handler)25         {26             Monitor.Enter(m_events);27             Delegate d;28             //TryGetValue确保在尝试从集合中删除不存在的EventKey时不会抛出异常29             if (m_events.TryGetValue(eventKey,out d))30             {31                 d = Delegate.Remove(d, handler);32                 if(d!=null)33                 {34                     //如果还有委托,就设置新的头部35                     m_events[eventKey] = d;36                 }37                 else38                 {39                     m_events.Remove(eventKey);40                 }41             }42             Monitor.Exit(m_events);43         }44         /// <summary>45         /// 为指定的EventKey引发事件46         /// </summary>       47         public void Raise(EventKey eventKey,Object sender,EventArgs e)48         {49             Delegate d;50             Monitor.Enter(m_events);51             m_events.TryGetValue(eventKey, out d);52             Monitor.Exit(m_events);53             if(d!=null)54             {55                 //利用DynamicInvoke,会向调用的回调方法查证参数的类型安全,56                 //并调用方法,如果存在类型不匹配,就抛异常57                 d.DynamicInvoke(new Object[] { sender, e });58             }59         }60     }
Nach dem Login kopieren

接下来定义类来使用EventSet


 1 public class FooEventArgs : EventArgs { } 2     public class TypeWithLotsOfEvents 3     { 4         //用于管理一组"事件/委托" 5         private readonly EventSet m_eventSet = new EventSet(); 6         //受保护的属性使派生类型能访问集合 7         protected EventSet EventSet { get { return m_eventSet; } } 8         //构造一个静态只读对象来标识这个事件 9         //每个对象都有自己的哈希码,以便在对象的集合中查找这个事件的委托链表10         protected static readonly EventKey s_fooEventKey = new EventKey();11         //定义事件访问器方法,用于在集合中增删委托12         public event EventHandler<FooEventArgs> Foo13         {14             add { m_eventSet.Add(s_fooEventKey, value); }15             remove { m_eventSet.Remove(s_fooEventKey, value); }16         }17         //为这个事件定义受保护的虚方法18         protected virtual void OnFoo(FooEventArgs e)19         {20             m_eventSet.Raise(s_fooEventKey, this, e);21         }22         //定义将输入转换成这个事件的方法23         public void SimulateFoo() { OnFoo(new FooEventArgs()); }24     }
Nach dem Login kopieren

如何使用TypeWithLotsOfEvent,只需按照标准的语法向事件登记即可


 1 static void Main(string[] args) 2         {           
 3             TypeWithLotsOfEvents twle = new TypeWithLotsOfEvents(); 4             twle.Foo += HandleFooEvent; 5             twle.SimulateFoo(); 6             Console.Read(); 7         } 8  9         private static void HandleFooEvent(object sender, FooEventArgs e)10         {11             Console.WriteLine("成功");12         }
Nach dem Login kopieren

 

Das obige ist der detaillierte Inhalt vonC#-Reihe von Artikelereignissen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage
Über uns Haftungsausschluss Sitemap
Chinesische PHP-Website:Online-PHP-Schulung für das Gemeinwohl,Helfen Sie PHP-Lernenden, sich schnell weiterzuentwickeln!