سؤال

أفهم الغرض من الأحداث، خاصة في سياق إنشاء واجهات المستخدم.أعتقد أن هذا هو النموذج الأولي لإنشاء حدث:

public void EventName(object sender, EventArgs e);

ماذا تفعل معالجات الأحداث، ولماذا هناك حاجة إليها، وكيف يمكنني إنشاء واحدة؟

هل كانت مفيدة؟

المحلول

لفهم معالجات الأحداث، تحتاج إلى فهم . في C # ، يمكنك أن تفكر مندوب كمؤشر (أو إشارة) إلى أسلوب. وهذا مفيد لأن مؤشر يمكن أن تنتقل حول كقيمة.

وهذا المفهوم المركزي لمندوب وفي التوقيع، أو الشكل. هذا هو (1) نوع الإرجاع و (2) الحجج الإدخال. على سبيل المثال، إذا كان لنا أن إنشاء void MyDelegate(object sender, EventArgs e) مندوب، فإنه يمكن أن نشير فقط إلى الأساليب التي تعود void، واتخاذ object وEventArgs. وكأنه نوع من حفرة مربعة وربط مربع. لذلك نحن نقول هذه الطرق لها نفس التوقيع، أو شكل، والمندوب.

وحتى معرفة كيفية إنشاء إشارة إلى طريقة، دعونا نفكر في أسباب تتعلق بالأحداث: نريد أن تسبب بعض التعليمات البرمجية ليتم تنفيذها عندما يحدث شيء في أي مكان آخر في النظام - أو "التعامل مع هذا الحدث". للقيام بذلك، ونحن خلق أساليب محددة لرمز نريد ليتم تنفيذها. الغراء بين الحدث والأساليب ليتم تنفيذها هم مندوبو. الحدث يجب تخزين داخليا "قائمة" من المؤشرات إلى وسائل للاتصال عندما يتم رفع هذا الحدث. * وبطبيعة الحال، لتكون قادرة على استدعاء أسلوب، نحن بحاجة إلى معرفة ما هي الحجج لتمرير لذلك! نحن نستخدم مندوب باسم "العقد" بين الحدث وجميع الطرق المحددة التي سوف يتم استدعاؤها.

وهكذا EventHandler الافتراضي (وكثير مثل ذلك) يمثل <م> شكل معين من طريقة (مرة أخرى، لاغية / الكائن EventArgs). عندما تقوم بتعريف حدث، أنت تقول <م> الذي شكل طريقة (EventHandler) وهذا الحدث الاحتجاج، عن طريق تحديد مندوب:

//This delegate can be used to point to methods
//which return void and take a string.
public delegate void MyEventHandler(string foo);

//This event can cause any method which conforms
//to MyEventHandler to be called.
public event MyEventHandler SomethingHappened;

//Here is some code I want to be executed
//when SomethingHappened fires.
void HandleSomethingHappened(string foo)
{
    //Do some stuff
}

//I am creating a delegate (pointer) to HandleSomethingHappened
//and adding it to SomethingHappened's list of "Event Handlers".
myObj.SomethingHappened += new MyEventHandler(HandleSomethingHappened);

//To raise the event within a method.
SomethingHappened("bar");

و(* هذا هو مفتاح الأحداث في. NET والقشور بعيدا "السحر" - حدث حقا، تحت الأغطية، مجرد قائمة من أساليب نفس "شكل" يتم تخزين قائمة حيث الحدث يعيش، وعندما حدث هو "رفعت"، انها حقا مجرد "تذهب من خلال هذه القائمة من أساليب وندعو كل واحد، وذلك باستخدام هذه القيم كمعلمات". تعيين معالج حدث هو مجرد طريقة أجمل أسهل إضافة طريقة لهذا قائمة الأساليب ليتم استدعاؤها).

نصائح أخرى

يعرف C# مصطلحين، delegate و event.لنبدأ بالأول.

مندوب

أ delegate هو إشارة إلى الأسلوب.تمامًا مثلما يمكنك إنشاء مرجع لمثيل:

MyClass instance = myFactory.GetInstance();

يمكنك استخدام المفوض لإنشاء مرجع لطريقة ما:

Action myMethod = myFactory.GetInstance;

الآن بعد أن أصبح لديك هذا المرجع للتابع، يمكنك استدعاء التابع عبر المرجع:

MyClass instance = myMethod();

ولكن لماذا؟يمكنك أيضًا الاتصال فقط myFactory.GetInstance() مباشرة.في هذه الحالة يمكنك.ومع ذلك، هناك العديد من الحالات التي يجب التفكير فيها والتي لا تريد أن يعرفها باقي التطبيق myFactory أو للاتصال myFactory.GetInstance() مباشرة.

الأمر الواضح هو إذا كنت تريد أن تكون قادرًا على الاستبدال myFactory.GetInstance() داخل myOfflineFakeFactory.GetInstance() من مكان مركزي واحد (ويعرف أيضًا باسم نمط طريقة المصنع).

نمط طريقة المصنع

لذلك، إذا كان لديك TheOtherClass فئة وتحتاج إلى استخدام myFactory.GetInstance(), ، هذه هي الطريقة التي سيبدو بها الكود بدون المفوضين (ستحتاج إلى السماح TheOtherClass تعرف على نوع الخاص بك myFactory):

TheOtherClass toc;
//...
toc.SetFactory(myFactory);


class TheOtherClass
{
   public void SetFactory(MyFactory factory)
   {
      // set here
   }

}

إذا كنت ستستخدم المندوبين، فلن تضطر إلى الكشف عن نوع المصنع الخاص بي:

TheOtherClass toc;
//...
Action factoryMethod = myFactory.GetInstance;
toc.SetFactoryMethod(factoryMethod);


class TheOtherClass
{
   public void SetFactoryMethod(Action factoryMethod)
   {
      // set here
   }

}

وبالتالي، يمكنك إعطاء مندوب إلى فئة أخرى لاستخدامه، دون تعريض نوعك لهم.الشيء الوحيد الذي تكشفه هو توقيع طريقتك (كم عدد المعلمات لديك وما إلى ذلك).

"توقيع طريقتي"، أين سمعت ذلك من قبل؟يا نعم واجهات!!!تصف الواجهات توقيع الفصل بأكمله.فكر في المندوبين على أنهم يصفون التوقيع بطريقة واحدة فقط!

هناك اختلاف كبير آخر بين الواجهة والمفوض وهو أنه عندما تكتب فصلك الدراسي، لا يتعين عليك أن تقول لـ C# "هذه الطريقة تنفذ هذا النوع من المفوضين".بالنسبة للواجهات، يجب أن تقول "هذه الفئة تنفذ هذا النوع من الواجهة".

علاوة على ذلك، يمكن لمرجع المفوض (مع بعض القيود، انظر أدناه) الإشارة إلى طرق متعددة (تسمى MulticastDelegate).هذا يعني أنه عند استدعاء المفوض، سيتم تنفيذ عدة طرق مرتبطة بشكل صريح.يمكن لمرجع الكائن دائمًا أن يشير إلى كائن واحد فقط.

القيود على أ MulticastDelegate هي أن توقيع (الطريقة/المندوب) يجب ألا يحتوي على أي قيمة إرجاع (void) والكلمات الرئيسية out و ref لا يستخدم في التوقيعمن الواضح أنه لا يمكنك استدعاء طريقتين تقومان بإرجاع رقم وتتوقع منهما إرجاع نفس الرقم.بمجرد امتثال التوقيع، يصبح المفوض تلقائيًا أ MulticastDelegate.

حدث

الأحداث هي مجرد خصائص (مثل get;set;خصائص لحقول المثيل) التي تعرض الاشتراك للمفوض من كائنات أخرى.ومع ذلك، لا تدعم هذه الخصائص get;set;.بدلاً من ذلك، يدعمون الإضافة؛يزيل؛

لذلك يمكنك الحصول على:

    Action myField;

    public event Action MyProperty
    {
        add { myField += value; }
        remove { myField -= value; }
    }

الاستخدام في واجهة المستخدم (WinForms،WPF،UWP، وما إلى ذلك)

إذن، نحن نعلم الآن أن المفوض هو مرجع لطريقة ما، وأنه يمكن أن يكون لدينا حدث لإعلام العالم بأنه يمكنهم إعطائنا أساليبهم ليتم الرجوع إليها من مندوبنا، ونحن زر واجهة المستخدم، إذن:يمكننا أن نطلب من أي شخص مهتم بمعرفة ما إذا تم النقر علي أن يسجل طريقته معنا (من خلال الحدث الذي كشفناه).يمكننا استخدام كل تلك الطرق التي تم تقديمها لنا والإشارة إليها من قبل مندوبنا.وبعد ذلك سننتظر وننتظر....حتى يأتي المستخدم وينقر على هذا الزر، سيكون لدينا سبب كافٍ لاستدعاء المفوض.ولأن المفوض يشير إلى كل تلك التوابع المعطاة لنا، فسيتم استدعاء كل تلك التوابع.لا نعرف ما الذي تفعله هذه الأساليب، ولا نعرف أي فئة تنفذ هذه الأساليب.كل ما نهتم به هو أن شخصًا ما كان مهتمًا بأن يتم النقر علينا، وأعطانا إشارة إلى الطريقة التي تتوافق مع التوقيع المطلوب.

جافا

لغات مثل Java ليس لديها مندوبين.يستخدمون واجهات بدلاً من ذلك.الطريقة التي يفعلون بها ذلك هي أن يطلبوا من أي شخص مهتم بـ "النقر علينا"، تنفيذ واجهة معينة (بطريقة معينة يمكننا الاتصال بها)، ثم إعطائنا المثيل الكامل الذي ينفذ الواجهة.نحتفظ بقائمة بجميع الكائنات التي تنفذ هذه الواجهة ويمكننا استدعاء "طريقة معينة يمكننا الاتصال بها" عندما يتم النقر عليها.

وهنا هو مثال التعليمة البرمجية التي قد تساعد على:

using System;
using System.Collections.Generic;
using System.Text;

namespace Event_Example
{
  // First we have to define a delegate that acts as a signature for the
  // function that is ultimately called when the event is triggered.
  // You will notice that the second parameter is of MyEventArgs type.
  // This object will contain information about the triggered event.

  public delegate void MyEventHandler(object source, MyEventArgs e);

  // This is a class which describes the event to the class that receives it.
  // An EventArgs class must always derive from System.EventArgs.

  public class MyEventArgs : EventArgs
  {
    private string EventInfo;

    public MyEventArgs(string Text) {
      EventInfo = Text;
    }

    public string GetInfo() {
      return EventInfo;
    }
  }

  // This next class is the one which contains an event and triggers it
  // once an action is performed. For example, lets trigger this event
  // once a variable is incremented over a particular value. Notice the
  // event uses the MyEventHandler delegate to create a signature
  // for the called function.

  public class MyClass
  {
    public event MyEventHandler OnMaximum;

    private int i;
    private int Maximum = 10;

    public int MyValue
    {
      get { return i; }
      set
      {
        if(value <= Maximum) {
          i = value;
        }
        else 
        {
          // To make sure we only trigger the event if a handler is present
          // we check the event to make sure it's not null.
          if(OnMaximum != null) {
            OnMaximum(this, new MyEventArgs("You've entered " +
              value.ToString() +
              ", but the maximum is " +
              Maximum.ToString()));
          }
        }
      }
    }
  }

  class Program
  {
    // This is the actual method that will be assigned to the event handler
    // within the above class. This is where we perform an action once the
    // event has been triggered.

    static void MaximumReached(object source, MyEventArgs e) {
      Console.WriteLine(e.GetInfo());
    }

    static void Main(string[] args) {
      // Now lets test the event contained in the above class.
      MyClass MyObject = new MyClass();
      MyObject.OnMaximum += new MyEventHandler(MaximumReached);
      for(int x = 0; x <= 15; x++) {
        MyObject.MyValue = x;
      }
      Console.ReadLine();
    }
  }
}

وهذا هو في الواقع إعلان عن معالج حدث - وسيلة من شأنها أن الحصول على استدعاء عندما يتم تشغيل الحدث. لإنشاء حدث، وكنت أكتب شيئا من هذا القبيل:

public class Foo
{
    public event EventHandler MyEvent;
}

وبعد ذلك يمكنك الاشتراك في مثل هذا الحدث:

Foo foo = new Foo();
foo.MyEvent += new EventHandler(this.OnMyEvent);

ومع OnMyEvent () المعرفة مثل هذا:

private void OnMyEvent(object sender, EventArgs e)
{
    MessageBox.Show("MyEvent fired!");
}

وعندما يطلق Foo من MyEvent، ثم سيتم استدعاء معالج OnMyEvent الخاص بك.

وليس لديك دائما لاستخدام مثيل EventArgs كمعلمة الثاني. إذا كنت تريد أن تدرج معلومات إضافية، يمكنك استخدام فئة مشتقة من EventArgs (EventArgs هو قاعدة من الاتفاقية). على سبيل المثال، إذا نظرتم الى بعض الأحداث المعرفة على Control في WinForms عناصر، أو FrameworkElement في WPF، يمكنك ان ترى أمثلة من الأحداث التي تمر معلومات إضافية إلى معالجات الأحداث.

وفقط لإضافة إلى إجابات كبيرة موجودة هنا - بناء على التعليمات البرمجية في واحدة مقبولة، والذي يستخدم delegate void MyEventHandler(string foo) ...

ولأن المترجم يعرف نوع مندوب <م> SomethingHappened الحدث، وهذا:

myObj.SomethingHappened += HandleSomethingHappened;

هل ما يعادل تماما ل:

myObj.SomethingHappened += new MyEventHandler(HandleSomethingHappened);

ومعالجات يمكن أيضا أن تكون <م> غير مسجلة مع -= مثل هذا:

// -= removes the handler from the event's list of "listeners":
myObj.SomethingHappened -= HandleSomethingHappened;

لأجل اكتمال، رفع الحدث ويمكن أن يتم مثل هذا، إلا في الطبقة التي تملك الحدث:

//Firing the event is done by simply providing the arguments to the event:
var handler = SomethingHappened; // thread-local copy of the event
if (handler != null) // the event is null if there are no listeners!
{
    handler("Hi there!");
}

وهناك حاجة لنسخ موضوع المحلي للمعالج للتأكد من أن الاحتجاج هو الخيط آمنة - على خلاف ذلك الموضوع يمكن أن تذهب وإلغاء تسجيل معالج الماضي لهذا الحدث على الفور بعد أن فحص ما اذا كان null، وسيكون لدينا "متعة" NullReferenceException هناك.


وC # 6 قدم يد قصيرة لطيفة لهذا النمط. ويستخدم المشغل لاغية نشر.

SomethingHappened?.Invoke("Hi there!");

فهمي للأحداث هو؛

مندوب:

متغير للاحتفاظ بالإشارة إلى الطريقة/الطرق التي سيتم تنفيذها.وهذا يجعل من الممكن تمرير أساليب مثل المتغير.

خطوات إنشاء الحدث واستدعاءه:

  1. الحدث هو مثيل للمفوض

  2. نظرًا لأن الحدث هو مثيل للمفوض، فيجب علينا أولاً تعريف المفوض.

  3. قم بتعيين الطريقة/الطرق التي سيتم تنفيذها عند إطلاق الحدث (استدعاء المندوب)

  4. إطلاق الحدث (اتصل بالمندوب)

مثال:

using System;

namespace test{
    class MyTestApp{
        //The Event Handler declaration
        public delegate void EventHandler();

        //The Event declaration
        public event EventHandler MyHandler;

        //The method to call
        public void Hello(){
            Console.WriteLine("Hello World of events!");
        }

        public static void Main(){
            MyTestApp TestApp = new MyTestApp();

            //Assign the method to be called when the event is fired
            TestApp.MyHandler = new EventHandler(TestApp.Hello);

            //Firing the event
            if (TestApp.MyHandler != null){
                TestApp.MyHandler();
            }
        }

    }   

}

والناشر: حيث تحدث الأحداث. الناشر ينبغي أن يحدد أي مندوب الطبقة يستخدم وتوليد الحجج اللازمة، وتمرير تلك الحجج ونفسها إلى المفوض.

والمشترك: حيث تحدث الاستجابة. المشترك ينبغي أن يحدد أساليب للرد على الأحداث. وينبغي لهذه الأساليب تأخذ نفس النوع من الحجج كما المندوب. المشترك ثم إضافة هذا الأسلوب لمندوب الناشر.

لذلك، عندما يحدث هذا الحدث في الناشر، مندوب سيحصل بعض الحجج الحدث (بيانات، الخ)، ولكن الناشر ليس لديه فكرة عما سيحدث مع كل هذه البيانات. ويمكن للمشتركين إنشاء طرق في فئتها الخاصة للرد على الأحداث في الصف الناشر، بحيث يمكن للمشتركين لرد على الأحداث الناشر.

//This delegate can be used to point to methods
//which return void and take a string.
public delegate void MyDelegate(string foo);

//This event can cause any method which conforms
//to MyEventHandler to be called.
public event MyDelegate MyEvent;

//Here is some code I want to be executed
//when SomethingHappened fires.
void MyEventHandler(string foo)
{
    //Do some stuff
}

//I am creating a delegate (pointer) to HandleSomethingHappened
//and adding it to SomethingHappened's list of "Event Handlers".
myObj.MyEvent += new MyDelegate (MyEventHandler);

وأنا أتفق مع KE50 إلا أنني عرض "الحدث" الكلمة كاسم مستعار ل 'ActionCollection "منذ هذا الحدث يحمل مجموعة من الإجراءات التي يتعين القيام بها (أي مندوب).

using System;

namespace test{

class MyTestApp{
    //The Event Handler declaration
    public delegate void EventAction();

    //The Event Action Collection 
    //Equivalent to 
    //  public List<EventAction> EventActions=new List<EventAction>();
    //        
    public event EventAction EventActions;

    //An Action
    public void Hello(){
        Console.WriteLine("Hello World of events!");
    }
    //Another Action
    public void Goodbye(){
        Console.WriteLine("Goodbye Cruel World of events!");
    }

    public static void Main(){
        MyTestApp TestApp = new MyTestApp();

        //Add actions to the collection
        TestApp.EventActions += TestApp.Hello;
        TestApp.EventActions += TestApp.Goodbye;

        //Invoke all event actions
        if (TestApp.EventActions!= null){
            //this peculiar syntax hides the invoke 
            TestApp.EventActions();
            //using the 'ActionCollection' idea:
            // foreach(EventAction action in TestApp.EventActions)
            //     action.Invoke();
        }
    }

}   

}

والأجوبة التقنية الكبرى في مرحلة ما بعد! ليس لدي أي شيء على من الناحية الفنية لإضافة على ذلك.

واحدة من الأسباب الرئيسية لتظهر الميزات الجديدة في اللغات والبرمجيات بشكل عام هو التسويق أو شركة السياسة! :-) هذا يجب ألا تكون تحت يقدر!

وأعتقد أن هذا ينطبق على بعض تمتد إلى المندوبين والأحداث أيضا! أجد من المفيد لهم وإضافة قيمة إلى C # لغة، ولكن من ناحية أخرى لغة جافا قررت عدم استخدامها! قرروا أن كل ما كنت حل مع مندوبي يمكنك حل بالفعل مع ميزات القائمة للغة أي واجهات مثل

والآن حوالي 2001 أصدرت Microsoft .NET Framework و لغة C # كحل منافس لجافا، لذلك كان من الجيد أن يكون لديك ميزات جديدة أن جافا لا يكون.

لقد قمت مؤخرًا بعمل مثال لكيفية استخدام الأحداث في لغة C#، ونشرته على مدونتي.حاولت توضيح الأمر قدر الإمكان بمثال بسيط للغاية.في حالة أنه قد يساعد أي شخص، هنا هو: http://www.konsfik.com/using-events-in-csharp/

يتضمن الوصف والكود المصدري (مع الكثير من التعليقات)، ويركز بشكل أساسي على الاستخدام المناسب (مثل القالب) للأحداث ومعالجات الأحداث.

بعض النقاط الرئيسية هي:

  • الأحداث تشبه "الأنواع الفرعية من المندوبين"، ولكنها أكثر تقييدًا (بطريقة جيدة).في الواقع، يتضمن إعلان الحدث دائمًا مندوبًا (يعد EventHandlers نوعًا من المفوضين).

  • معالجات الأحداث هي أنواع محددة من المفوضين (قد تفكر فيها كقالب)، والتي تجبر المستخدم على إنشاء أحداث لها "توقيع" محدد.التوقيع يكون بالصيغة :(مرسل الكائن، وسيطات الأحداث EventArgs).

  • يمكنك إنشاء فئة فرعية خاصة بك من EventArgs، لتضمين أي نوع من المعلومات التي يحتاج الحدث إلى نقلها.ليس من الضروري استخدام EventHandlers عند استخدام الأحداث.يمكنك تخطيهم تمامًا واستخدام نوع مندوب خاص بك بدلاً منهم.

  • أحد الاختلافات الرئيسية بين استخدام الأحداث والمفوضين هو أنه لا يمكن استدعاء الأحداث إلا من داخل الفئة التي تم الإعلان عنها، على الرغم من أنه قد يتم الإعلان عنها كعامة.وهذا تمييز مهم للغاية، لأنه يسمح بكشف الأحداث الخاصة بك بحيث تكون "متصلة" بالطرق الخارجية، وفي نفس الوقت تكون محمية من "سوء الاستخدام الخارجي".

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top