การทำความเข้าใจเหตุการณ์และตัวจัดการเหตุการณ์ใน C #


330

ฉันเข้าใจวัตถุประสงค์ของเหตุการณ์โดยเฉพาะอย่างยิ่งในบริบทของการสร้างส่วนต่อประสานผู้ใช้ ฉันคิดว่านี่เป็นต้นแบบสำหรับการสร้างกิจกรรม:

public void EventName(object sender, EventArgs e);

ตัวจัดการเหตุการณ์ทำอะไรทำไมพวกเขาต้องการและฉันจะสร้างได้อย่างไร


10
ตามที่ระบุไว้โดย @Andy โค้ดขนาดเล็กที่นี่จะอธิบายวิธีการที่ลงทะเบียนกับเหตุการณ์ไม่ใช่ตัวเหตุการณ์เอง
dthrasher


คำตอบ:


661

เพื่อให้เข้าใจถึงจัดการเหตุการณ์ที่คุณต้องเข้าใจผู้ได้รับมอบหมาย ในC #คุณสามารถนึกถึงผู้รับมอบสิทธิ์เป็นตัวชี้ (หรือการอ้างอิง) ไปยังวิธีการ สิ่งนี้มีประโยชน์เนื่องจากตัวชี้สามารถส่งผ่านเป็นค่าได้

แนวคิดหลักของผู้ร่วมประชุมคือลายเซ็นหรือรูปร่าง นั่นคือ (1) ชนิดส่งคืนและ (2) อาร์กิวเมนต์อินพุต ตัวอย่างเช่นถ้าเราสร้างตัวแทนvoid MyDelegate(object sender, EventArgs e)ก็สามารถชี้ไปที่วิธีการที่กลับมาvoidและใช้เวลาและobject EventArgsชนิดของรูสี่เหลี่ยมและหมุดสี่เหลี่ยม ดังนั้นเราจึงบอกว่าวิธีการเหล่านี้มีลายเซ็นหรือรูปร่างเหมือนกันกับผู้ร่วม

ดังนั้นเมื่อรู้วิธีสร้างการอ้างอิงไปยังเมธอดลองคิดเกี่ยวกับวัตถุประสงค์ของเหตุการณ์: เราต้องการทำให้โค้ดบางอย่างถูกเรียกใช้เมื่อมีบางสิ่งเกิดขึ้นที่อื่นในระบบ - หรือ "จัดการกับเหตุการณ์" ในการทำเช่นนี้เราสร้างวิธีการเฉพาะสำหรับรหัสที่เราต้องการเรียกใช้ กาวระหว่างเหตุการณ์และวิธีการที่จะดำเนินการเป็นตัวแทน เหตุการณ์จะต้องเก็บ "รายการ" ของพอยน์เตอร์ภายในเมธอดที่จะเรียกเมื่อมีการยกระดับเหตุการณ์ * แน่นอนเพื่อให้สามารถเรียกเมธอดได้เราจำเป็นต้องรู้ว่าอาร์กิวเมนต์ใดที่ต้องผ่านไป! เราใช้ผู้รับมอบสิทธิ์เป็น "สัญญา" ระหว่างกิจกรรมและวิธีการเฉพาะทั้งหมดที่จะถูกเรียก

ดังนั้นค่าเริ่มต้นEventHandler(และชอบมาก) แสดงให้เห็นถึงรูปร่างที่เฉพาะเจาะจงของวิธีการ (อีกครั้งเป็นโมฆะ / object-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 และลอก "magic" ออกไป - เหตุการณ์เป็นจริงภายใต้หน้าปกเพียงรายการวิธีการที่มีรูปร่าง "เดียวกัน" รายการจะถูกเก็บไว้ที่เหตุการณ์อาศัยอยู่เมื่อ เหตุการณ์คือ "ฟื้นคืนชีพขึ้นมา" จริง ๆ แล้วเป็นเพียง "ผ่านรายการวิธีการนี้และเรียกแต่ละรายการโดยใช้ค่าเหล่านี้เป็นพารามิเตอร์" การกำหนดตัวจัดการเหตุการณ์เป็นเพียงวิธีที่สวยกว่าและง่ายกว่าในการเพิ่มวิธีการของคุณไปยังรายการวิธีการนี้ จะถูกเรียก)


24
และตอนนี้ทุกคนสามารถอธิบายได้ว่าเหตุใดเหตุการณ์นี้จึงถูกเรียกว่า EventHandler ของทุกการประชุมทำให้เกิดความสับสนในการตั้งชื่อนี้เป็นที่เลวร้ายที่สุด ...
โจเอลไปใน

37
@Joel in Go กิจกรรมไม่ได้เรียกว่า EventHandler - EventHandler เป็นสัญญาที่กิจกรรมต้องมีกับทุกคนที่สื่อสารกับมัน มันเหมือนกับ "string MyString" - สตริงกำลังประกาศประเภท เหตุการณ์ MyEventHandler TheEvent ประกาศว่าทุกคนที่มีปฏิสัมพันธ์กับเหตุการณ์นี้จะต้องเป็นไปตามสัญญา MyEventHandler หลักการจัดการเป็นเพราะสัญญาอธิบายถึงวิธีจัดการเหตุการณ์เป็นหลัก
Rex M

18
เหตุการณ์นี้เกิดขึ้นได้อย่างไร
เล่นแร่แปรธาตุ

17
@Rex M: ขอบคุณสำหรับคำอธิบายแรกที่เกี่ยวข้องกันสำหรับ "MyEventHandler" ที่ฉันเคยเห็น :)
Joel ในGö

10
ขอบคุณสำหรับเฟส: "กาวระหว่างเหตุการณ์และวิธีการที่จะดำเนินการเป็นผู้ได้รับมอบหมาย" นี่มันยอดเยี่ยมจริงๆ
zionpi

103

C # รู้คำสองคำ, และdelegate eventเริ่มจากอันแรกกันเถอะ

ตัวแทน

A delegateเป็นการอ้างอิงถึงวิธีการ เช่นเดียวกับที่คุณสามารถสร้างการอ้างอิงไปยังอินสแตนซ์:

MyClass instance = myFactory.GetInstance();

คุณสามารถใช้ผู้รับมอบสิทธิ์เพื่อสร้างการอ้างอิงถึงวิธีการ:

Action myMethod = myFactory.GetInstance;

ตอนนี้คุณมีการอ้างอิงถึงเมธอดคุณสามารถเรียกใช้เมธอดผ่านการอ้างอิง:

MyClass instance = myMethod();

แต่ทำไมล่ะ คุณสามารถโทรmyFactory.GetInstance()โดยตรงได้ ในกรณีนี้คุณสามารถ อย่างไรก็ตามมีหลายกรณีที่ต้องพิจารณาว่าคุณไม่ต้องการให้แอปพลิเคชั่นที่เหลือมีความรู้myFactoryหรือโทรmyFactory.GetInstance()โดยตรง

เห็นได้ชัดอย่างใดอย่างหนึ่งคือถ้าคุณต้องการที่จะสามารถที่จะเปลี่ยนmyFactory.GetInstance()ลงไปmyOfflineFakeFactory.GetInstance()จากสถานที่หนึ่งกลาง (aka รูปแบบการโรงงาน )

รูปแบบวิธีการของโรงงาน

ดังนั้นถ้าคุณมี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) ซึ่งหมายความว่าเมื่อคุณโทรหาผู้ร่วมประชุมจะมีการดำเนินการวิธีการหลายวิธีที่แนบมาอย่างชัดเจน การอ้างอิงวัตถุสามารถอ้างอิงได้เพียงหนึ่งวัตถุเท่านั้น

ข้อ จำกัด สำหรับ a MulticastDelegateคือลายเซ็น (method / ผู้รับมอบสิทธิ์) ไม่ควรมีค่าส่งคืน ( void) และคำหลักoutและrefไม่ได้ใช้ในลายเซ็น เห็นได้ชัดว่าคุณไม่สามารถเรียกสองวิธีที่ส่งคืนหมายเลขและคาดหวังให้ส่งคืนหมายเลขเดิม MulticastDelegateเมื่อลายเซ็นสอดคล้องที่ผู้ร่วมประชุมเป็นโดยอัตโนมัติ

เหตุการณ์

เหตุการณ์เป็นเพียงคุณสมบัติ (เช่น get; set; properties to field instance) ซึ่งแสดงการสมัครสมาชิกไปยังผู้รับมอบสิทธิ์จากวัตถุอื่น ๆ อย่างไรก็ตามคุณสมบัติเหล่านี้ไม่รองรับรับ; set; พวกเขาสนับสนุนการเพิ่ม; ลบ;

ดังนั้นคุณสามารถมี:

    Action myField;

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

การใช้งานใน UI (WinForms, WPF, UWP เป็นต้น)

ดังนั้นตอนนี้เรารู้ว่าผู้ได้รับมอบหมายเป็นการอ้างอิงถึงวิธีการและเราสามารถมีเหตุการณ์ที่จะทำให้โลกรู้ว่าพวกเขาสามารถให้วิธีการที่จะอ้างอิงจากผู้ได้รับมอบหมายของเราและเราเป็นปุ่ม UI จากนั้น: เรา สามารถถามใครก็ได้ที่สนใจว่าฉันคลิกเพื่อลงทะเบียนวิธีการของพวกเขากับเรา (ผ่านเหตุการณ์ที่เราสัมผัส) เราสามารถใช้วิธีการทั้งหมดที่ได้รับจากเราและอ้างอิงโดยตัวแทนของเรา จากนั้นเราจะรอและรอจนกว่าผู้ใช้จะมาถึงและคลิกที่ปุ่มจากนั้นเราจะมีเหตุผลเพียงพอที่จะเรียกผู้แทน และเนื่องจากผู้ได้รับมอบหมายอ้างอิงวิธีการทั้งหมดที่ให้ไว้กับเราวิธีการเหล่านั้นทั้งหมดจะถูกเรียกใช้ เราไม่รู้ว่าวิธีการเหล่านั้นทำอะไรและเราไม่รู้ว่าชั้นเรียนใดใช้วิธีการเหล่านั้น สิ่งที่เราใส่ใจคือคนที่สนใจเราถูกคลิก

ชวา

ภาษาอย่าง Java ไม่มีผู้รับมอบสิทธิ์ พวกเขาใช้อินเทอร์เฟซแทน วิธีที่พวกเขาทำคือขอให้ทุกคนที่สนใจใน 'เราถูกคลิก' เพื่อใช้อินเทอร์เฟซบางอย่าง (ด้วยวิธีการบางอย่างที่เราสามารถเรียกได้) จากนั้นให้อินสแตนซ์ทั้งหมดที่ใช้อินเทอร์เฟซ เราเก็บรายการของวัตถุทั้งหมดที่ใช้อินเทอร์เฟซนี้และสามารถเรียก 'วิธีการบางอย่างที่เราสามารถเรียกได้' ทุกครั้งที่เราคลิก


ให้กำลังใจคำอธิบาย แต่เหตุการณ์ต่างจากอินสแตนซ์ของผู้รับมอบสิทธิ์ที่ทำกับสมาชิกอย่างไร พวกเขาทั้งสองดูเหมือนกันหรือไม่
BKSpurgeon

@BKSpurgeon นั่นเป็นเพราะพวกเขาเป็น "ผู้ได้รับมอบหมายที่รับสมาชิก" - eventเป็นเพียงน้ำตาลซินแท็กซ์ไม่มีอะไรเพิ่มเติม
Mathieu Guindon

"ข้อ จำกัด สำหรับ MulticastDelegate คือลายเซ็น (method / ผู้รับมอบสิทธิ์) ไม่ควรมีค่าส่งคืน (เป็นโมฆะ)" ฉันไม่คิดว่ามันถูกต้อง หากพวกเขามีค่าตอบแทนมันจะกลับมาล่าสุด
Hozikimaru

"ดังนั้นคุณสามารถมอบสิทธิ์ให้ผู้อื่นในชั้นเรียนอื่น ๆ โดยไม่ต้องเปิดเผยประเภทของคุณให้พวกเขาสิ่งเดียวที่คุณเปิดเผยคือลายเซ็นของวิธีการของคุณ ... " - สำหรับฉันนั้นเป็นจุดสำคัญ ขอบคุณ!
Ryan

40

นั่นคือการประกาศสำหรับตัวจัดการเหตุการณ์ - วิธีการที่จะได้รับการเรียกเมื่อมีเหตุการณ์ถูกเรียกใช้ ในการสร้างกิจกรรมคุณต้องเขียนดังนี้:

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 คุณสามารถดูตัวอย่างของเหตุการณ์ที่ส่งผ่านข้อมูลเพิ่มเติมไปยังตัวจัดการเหตุการณ์


14
ขอขอบคุณที่ตอบคำถามและไม่เข้าร่วมเป็นตัวแทนและกิจกรรม
divide_byzero

3
ฉันอยากจะแนะนำให้ใช้OnXXXรูปแบบการตั้งชื่อสำหรับตัวจัดการเหตุการณ์ของคุณ (Stupidly, OnXXX ถูกนำมาใช้เพื่อหมายถึง 'handle XXX' ใน MFC และ 'เพิ่ม XXX' ใน. net ดังนั้นตอนนี้ความหมายของมันไม่ชัดเจนและสับสน - ดูโพสต์นี้สำหรับรายละเอียด ) ชื่อที่ต้องการจะRaiseXXXยกเหตุการณ์และHandleXXXหรือSender_XXXสำหรับตัวจัดการเหตุการณ์
Jason Williams

1
คุณสามารถแสดงตัวอย่างการทำงานด้วยแอปพลิเคชัน WinForms อย่างง่ายได้หรือไม่?
MC9000

40

นี่คือตัวอย่างของรหัสที่อาจช่วยได้:

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();
    }
  }
}

4
การเชิญตัวแทนใน C # 6 นั้นง่ายต่อการ:OnMaximum?.Invoke(this,new MyEventArgs("you've entered..."));
Tim Schmelter

23

เพียงเพิ่มคำตอบที่ยอดเยี่ยมที่มีอยู่ที่นี่ - สร้างรหัสในคำตอบที่ยอมรับซึ่งใช้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!");

13

ความเข้าใจของฉันเกี่ยวกับเหตุการณ์คือ;

มอบหมาย:

ตัวแปรที่เก็บการอ้างอิงถึงเมธอด / เมธอดที่ต้องถูกเรียกใช้งาน สิ่งนี้ทำให้เป็นไปได้ที่จะส่งผ่านวิธีต่างๆเช่นตัวแปร

ขั้นตอนสำหรับการสร้างและโทรเหตุการณ์:

  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();
            }
        }

    }   

}

3

ผู้เผยแพร่: เหตุการณ์ที่เกิดขึ้น ผู้เผยแพร่ควรระบุผู้รับมอบสิทธิ์ที่ใช้และสร้างอาร์กิวเมนต์ที่จำเป็นส่งผ่านอาร์กิวเมนต์เหล่านั้นและตัวเองไปยังผู้รับมอบสิทธิ์

สมาชิก: ที่การตอบสนองเกิดขึ้น สมาชิกควรระบุวิธีการในการตอบสนองต่อเหตุการณ์ วิธีการเหล่านี้ควรใช้อาร์กิวเมนต์ประเภทเดียวกันกับผู้ได้รับมอบหมาย จากนั้นผู้สมัครสมาชิกเพิ่มวิธีนี้ในการมอบสิทธิ์ของผู้เผยแพร่

ดังนั้นเมื่อเหตุการณ์เกิดขึ้นในผู้เผยแพร่ผู้รับมอบสิทธิ์จะได้รับข้อโต้แย้งบางเหตุการณ์ (ข้อมูลและอื่น ๆ ) แต่ผู้เผยแพร่ไม่ทราบว่าจะเกิดอะไรขึ้นกับข้อมูลทั้งหมดนี้ ผู้สมัครสมาชิกสามารถสร้างวิธีการในชั้นเรียนของตนเองเพื่อตอบสนองต่อกิจกรรมในชั้นเรียนของผู้เผยแพร่เพื่อให้สมาชิกสามารถตอบสนองต่อกิจกรรมของผู้จัดพิมพ์


2
//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);

0

ฉันเห็นด้วยกับ 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();
        }
    }

}   

}

0

คำตอบทางเทคนิคที่ยอดเยี่ยมในการโพสต์! ฉันไม่มีอะไรทางเทคนิคที่จะเพิ่มเข้าไป

หนึ่งในเหตุผลหลักที่คุณสมบัติใหม่ปรากฏในภาษาและซอฟต์แวร์โดยทั่วไปคือการตลาดหรือการเมืองของ บริษัท ! :-) สิ่งนี้จะต้องไม่ถูกประเมิน!

ฉันคิดว่าสิ่งนี้ใช้ได้กับผู้ที่ได้รับมอบหมายและเหตุการณ์เช่นกัน! ฉันพบว่ามีประโยชน์และเพิ่มคุณค่าให้กับภาษา C # แต่ในทางกลับกันภาษา Java ตัดสินใจที่จะไม่ใช้มัน! พวกเขาตัดสินใจว่าอะไรก็ตามที่คุณกำลังแก้ไขกับผู้ได้รับมอบหมายคุณสามารถแก้ไขด้วยคุณสมบัติที่มีอยู่ของภาษาเช่นอินเทอร์เฟซเช่น

ขณะนี้ประมาณปี 2544 ไมโครซอฟท์เปิดตัว. NET framework และภาษา C # เป็นโซลูชันของคู่แข่งใน Java ดังนั้นจึงเป็นการดีที่มีคุณสมบัติใหม่ที่ Java ไม่มี


0

ฉันเพิ่งทำตัวอย่างของวิธีการใช้เหตุการณ์ใน c # และโพสต์ไว้ในบล็อกของฉัน ฉันพยายามทำให้ชัดเจนที่สุดด้วยตัวอย่างง่ายๆ ในกรณีที่มันอาจช่วยให้ทุกคนนี่คือ: http://www.konsfik.com/using-events-in-csharp/

มันมีคำอธิบายและซอร์สโค้ด (ที่มีความคิดเห็นจำนวนมาก) และส่วนใหญ่จะเน้นที่การใช้งานที่เหมาะสม (เช่นเทมเพลต) ของเหตุการณ์และตัวจัดการเหตุการณ์

ประเด็นสำคัญบางประการคือ:

  • กิจกรรมเป็นเหมือน "ประเภทย่อยของผู้ได้รับมอบหมาย" มีข้อ จำกัด มากขึ้นเท่านั้น (ในทางที่ดี) ในความเป็นจริงการประกาศของกิจกรรมจะมีตัวแทนอยู่เสมอ (EventHandlers เป็นประเภทของผู้รับมอบสิทธิ์)

  • ตัวจัดการเหตุการณ์เป็นตัวแทนประเภทเฉพาะ (คุณอาจคิดว่าพวกเขาเป็นเทมเพลต) ซึ่งบังคับให้ผู้ใช้สร้างกิจกรรมที่มี "ลายเซ็น" ที่เฉพาะเจาะจง ลายเซ็นมีรูปแบบ: (ผู้ส่งวัตถุ, EventArgs เหตุการณ์อาร์กิวเมนต์)

  • คุณสามารถสร้างคลาสย่อยของ EventArgs เพื่อรวมข้อมูลประเภทใด ๆ ที่กิจกรรมต้องการสื่อ ไม่จำเป็นต้องใช้ตัวจัดการเหตุการณ์เมื่อใช้งานเหตุการณ์ คุณอาจข้ามพวกเขาไปอย่างสิ้นเชิงและใช้ตัวแทนของคุณเองแทน

  • ความแตกต่างที่สำคัญอย่างหนึ่งระหว่างการใช้กิจกรรมและผู้รับมอบสิทธิ์คือสามารถเรียกใช้เหตุการณ์จากภายในคลาสที่พวกเขาประกาศเท่านั้นแม้ว่าพวกเขาจะประกาศเป็นสาธารณะก็ตาม นี่คือความแตกต่างที่สำคัญมากเพราะจะทำให้กิจกรรมของคุณได้รับการเปิดเผยเพื่อให้พวกเขา "เชื่อมต่อ" กับวิธีการภายนอกในขณะที่พวกเขาได้รับการปกป้องจาก "การใช้ในทางที่ผิดภายนอก"


0

อีกสิ่งหนึ่งที่คุณควรรู้ในบางกรณีคุณต้องใช้ผู้ได้รับมอบหมาย / เหตุการณ์เมื่อคุณต้องการมีเพศสัมพันธ์ในระดับต่ำ !

หากคุณต้องการใช้ส่วนประกอบในสถานที่หลายแห่งในแอปพลิเคชันคุณต้องสร้างส่วนประกอบที่มีการเชื่อมต่อในระดับต่ำและLOGIC ที่ไม่เกี่ยวข้องนั้นจะต้องได้รับการมอบหมายจากด้านนอกของส่วนประกอบ! สิ่งนี้ทำให้มั่นใจได้ว่าคุณมีระบบแยกและรหัสทำความสะอาด

ในหลักการSOLIDนี่คือ " D ", ( Dหลักการผกผัน ependency)

หรือที่เรียกว่า " IoC " การควบคุมกลับกัน

คุณสามารถสร้าง " IoC " กับกิจกรรมผู้ได้รับมอบหมายและ DI (การพึ่งพาการฉีด)

ง่ายต่อการเข้าถึงวิธีการในชั้นเรียนเด็ก แต่ยากกว่าในการเข้าถึงวิธีการในชั้นผู้ปกครองจากเด็ก คุณต้องผ่านการอ้างอิงผู้ปกครองกับเด็ก! (หรือใช้ DI กับส่วนต่อประสาน)

ผู้ได้รับมอบหมาย / กิจกรรมช่วยให้เราสามารถสื่อสารจากเด็กไปยังผู้ปกครองโดยไม่มีการอ้างอิง!

ป้อนคำอธิบายรูปภาพที่นี่

ในแผนภาพข้างต้นนี้ฉันไม่ได้ใช้ตัวแทน / เหตุการณ์และองค์ประกอบหลัก B ต้องมีการอ้างอิงขององค์ประกอบหลัก A เพื่อดำเนินการตรรกะทางธุรกิจที่ไม่เกี่ยวข้องในวิธีการของ A. (การมีเพศสัมพันธ์ในระดับสูง)

ด้วยวิธีนี้ฉันจะต้องใส่การอ้างอิงทั้งหมดของส่วนประกอบทั้งหมดที่ใช้องค์ประกอบ B! :(

ป้อนคำอธิบายรูปภาพที่นี่

ในแผนภาพด้านบนนี้ฉันใช้ Delegate / Eventและส่วนประกอบ B ไม่จำเป็นต้องรู้จัก A. (การมีเพศสัมพันธ์ในระดับต่ำ)

และคุณสามารถใช้ส่วนประกอบ B ของคุณได้ทุกที่ในแอปพลิเคชันของคุณ !

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.