Memahami acara dan penangan acara di C #


330

Saya memahami tujuan acara, terutama dalam konteks membuat antarmuka pengguna. Saya pikir ini adalah prototipe untuk membuat acara:

public void EventName(object sender, EventArgs e);

Apa yang dilakukan oleh penangan acara, mengapa mereka dibutuhkan, dan bagaimana cara membuatnya?


10
Seperti dicatat oleh @Andy, potongan kode di sini menjelaskan metode yang terdaftar untuk acara tersebut, bukan acara itu sendiri.
dthrasher


Jawaban:


661

Untuk memahami event handler, Anda perlu memahami delegasi . Di C # , Anda bisa menganggap delegasi sebagai penunjuk (atau referensi) ke suatu metode. Ini berguna karena pointer dapat diedarkan sebagai nilai.

Konsep sentral dari delegasi adalah tanda tangannya, atau bentuknya. Yaitu (1) jenis kembali dan (2) argumen input. Misalnya, jika kita membuat delegasi void MyDelegate(object sender, EventArgs e), itu hanya bisa menunjuk ke metode yang kembali void, dan mengambil objectdan EventArgs. Jenisnya seperti lubang persegi dan pasak persegi. Jadi kami mengatakan metode ini memiliki tanda tangan atau bentuk yang sama dengan delegasi.

Jadi mengetahui cara membuat referensi ke suatu metode, mari kita pikirkan tentang tujuan acara: kami ingin menyebabkan beberapa kode dieksekusi ketika sesuatu terjadi di tempat lain dalam sistem - atau "menangani acara". Untuk melakukan ini, kami membuat metode khusus untuk kode yang ingin kami jalankan. Perekat antara acara dan metode yang akan dieksekusi adalah delegasi. Acara harus secara internal menyimpan "daftar" pointer ke metode untuk memanggil ketika acara dinaikkan. * Tentu saja, untuk dapat memanggil metode, kita perlu tahu argumen apa yang harus dilalui untuk itu! Kami menggunakan delegasi sebagai "kontrak" antara acara dan semua metode khusus yang akan dipanggil.

Jadi default EventHandler(dan banyak yang seperti itu) mewakili bentuk metode tertentu (sekali lagi, void / object-EventArgs). Ketika Anda mendeklarasikan suatu peristiwa, Anda mengatakan bentuk metode (EventHandler) mana yang akan dipanggil oleh acara, dengan menentukan delegasi:

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

(* Ini adalah kunci untuk peristiwa dalam .NET dan menghilangkan "sihir" - suatu peristiwa benar-benar, di bawah selimut, hanya daftar metode dari "bentuk" yang sama. Daftar ini disimpan di tempat acara tersebut berlangsung. Ketika acara "dinaikkan", itu benar-benar hanya "pergi melalui daftar metode ini dan memanggil masing-masing, menggunakan nilai-nilai ini sebagai parameter". Menetapkan event handler hanya lebih cantik, cara lebih mudah untuk menambahkan metode Anda ke daftar metode ini disebut).


24
Dan sekarang adakah yang bisa menjelaskan mengapa acara ini disebut EventHandler ?? Dari semua konvensi penamaan yang membingungkan, ini adalah yang terburuk ...
Joel di Gö

37
@ Joel in Go acara tidak disebut EventHandler - EventHandler adalah kontrak yang harus dimiliki oleh acara tersebut dengan siapa saja yang berkomunikasi dengannya. Ini seperti "string MyString" - string menyatakan tipe. event MyEventHandler TheEvent menyatakan bahwa siapa pun yang berinteraksi dengan acara ini harus mematuhi kontrak MyEventHandler. Konvensi Handler adalah karena kontrak terutama menjelaskan cara menangani acara tersebut.
Rex M

18
Bagaimana acara dipecat?
alkimia

17
@Rex M: terima kasih atas penjelasan koheren pertama untuk "MyEventHandler" yang pernah saya lihat :)
Joel di Gö

10
Terima kasih untuk fase: "Perekat antara acara dan metode yang akan dieksekusi adalah delegasi.", Ini benar-benar luar biasa.
zionpi

103

C # tahu dua istilah, delegatedan event. Mari kita mulai dengan yang pertama.

Melimpahkan

A delegateadalah referensi ke suatu metode. Sama seperti Anda dapat membuat referensi ke sebuah instance:

MyClass instance = myFactory.GetInstance();

Anda dapat menggunakan delegasi untuk membuat referensi ke metode:

Action myMethod = myFactory.GetInstance;

Sekarang Anda memiliki referensi ini ke suatu metode, Anda dapat memanggil metode tersebut melalui referensi:

MyClass instance = myMethod();

Tetapi mengapa Anda mau? Anda juga bisa myFactory.GetInstance()langsung menelepon . Dalam hal ini Anda bisa. Namun, ada banyak kasus untuk dipikirkan di mana Anda tidak ingin seluruh aplikasi memiliki pengetahuan myFactoryatau panggilanmyFactory.GetInstance() langsung.

Salah satu yang jelas adalah jika Anda ingin dapat menggantikan myFactory.GetInstance()menjadi myOfflineFakeFactory.GetInstance()dari satu tempat pusat (alias pola metode pabrik ).

Pola metode pabrik

Jadi, jika Anda memiliki TheOtherClasskelas dan perlu menggunakan myFactory.GetInstance(), ini adalah bagaimana kode akan terlihat seperti tanpa delegasi (Anda harus memberi TheOtherClasstahu tentang jenis Anda myFactory):

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


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

}

Jika Anda akan menggunakan delegasi, Anda tidak perlu mengekspos jenis pabrik saya:

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


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

}

Dengan demikian, Anda dapat memberikan delegasi ke beberapa kelas lain untuk digunakan, tanpa memaparkan tipe Anda kepada mereka. Satu-satunya hal yang Anda perlihatkan adalah tanda tangan dari metode Anda (berapa banyak parameter yang Anda miliki dan semacamnya).

"Tanda tangan metode saya", di mana saya mendengar itu sebelumnya? O ya, antarmuka !!! antarmuka menggambarkan tanda tangan seluruh kelas. Pikirkan delegasi sebagai menggambarkan tanda tangan hanya satu metode!

Perbedaan besar lainnya antara antarmuka dan delegasi adalah bahwa ketika Anda menulis kelas Anda, Anda tidak perlu mengatakan kepada C # "metode ini mengimplementasikan tipe delegasi itu". Dengan antarmuka, Anda perlu mengatakan "kelas ini mengimplementasikan jenis antarmuka".

Selanjutnya, referensi delegasi dapat (dengan beberapa batasan, lihat di bawah) referensi beberapa metode (dipanggil MulticastDelegate). Ini berarti bahwa ketika Anda memanggil delegasi, beberapa metode yang dilampirkan secara eksplisit akan dieksekusi. Referensi objek selalu hanya bisa merujuk ke satu objek.

Batasan untuk a MulticastDelegateadalah bahwa tanda tangan (metode / delegasi) tidak boleh memiliki nilai pengembalian ( void) dan kata kunci outdan reftidak digunakan dalam tanda tangan. Jelas, Anda tidak dapat memanggil dua metode yang mengembalikan nomor dan mengharapkan mereka untuk mengembalikan nomor yang sama. Setelah tanda tangan dipatuhi, delegasi secara otomatis a MulticastDelegate.

Peristiwa

Acara hanyalah properti (seperti get; set; properti ke instance field) yang mengekspos berlangganan ke delegasi dari objek lain. Namun, properti ini tidak mendukung get; set ;. Sebaliknya, mereka mendukung add; menghapus;

Jadi, Anda dapat memiliki:

    Action myField;

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

Penggunaan di UI (WinForms, WPF, UWP, dan sebagainya)

Jadi, sekarang kita tahu bahwa seorang delegasi adalah referensi ke suatu metode dan bahwa kita dapat memiliki acara untuk memberi tahu dunia bahwa mereka dapat memberi kita metode mereka untuk dirujuk dari delegasi kita, dan kita adalah tombol UI, lalu: kita dapat bertanya kepada siapa saja yang tertarik apakah saya diklik, untuk mendaftarkan metode mereka kepada kami (melalui acara yang kami paparkan). Kita dapat menggunakan semua metode yang diberikan kepada kita dan merujuknya oleh delegasi kita. Dan kemudian, kita akan menunggu dan menunggu .... sampai seorang pengguna datang dan mengklik tombol itu, maka kita akan memiliki cukup alasan untuk memohon delegasi. Dan karena delegasi merujuk semua metode yang diberikan kepada kami, semua metode itu akan dipanggil. Kami tidak tahu apa yang dilakukan metode-metode itu, kami juga tidak tahu kelas mana yang mengimplementasikan metode-metode itu. Yang kami pedulikan hanyalah seseorang tertarik pada kami diklik,

Jawa

Bahasa seperti Java tidak memiliki delegasi. Mereka menggunakan antarmuka sebagai gantinya. Cara mereka melakukannya adalah dengan meminta siapa saja yang tertarik pada 'kami diklik', untuk mengimplementasikan antarmuka tertentu (dengan metode tertentu yang dapat kita panggil), lalu berikan kami seluruh contoh yang mengimplementasikan antarmuka. Kami menyimpan daftar semua objek yang mengimplementasikan antarmuka ini dan dapat memanggil 'metode tertentu yang dapat kami panggil' setiap kali kami diklik.


bersorak untuk penjelasan tetapi bagaimana suatu peristiwa berbeda dari contoh delegasi yang mengambil pelanggan? mereka berdua terlihat seperti hal yang persis sama?
BKSpurgeon

@ BKSpurgeon itu karena mereka adalah "delegasi yang menerima pelanggan" - eventhanyalah gula sintaks, tidak lebih.
Mathieu Guindon

"Batasan untuk MulticastDelegate adalah tanda tangan (metode / delegasi) tidak boleh memiliki nilai balik (batal)", saya rasa ini tidak benar. Jika mereka memiliki nilai kembali, itu akan mengembalikan yang terakhir.
Hozikimaru

"Jadi, kamu bisa memberikan delegasi ke kelas lain untuk digunakan, tanpa memaparkan tipikmu kepada mereka. Satu-satunya hal yang kamu perlihatkan adalah tanda tangan dari metodemu ..." - itu bagiku adalah titik kritis. Terima kasih!
Ryan

40

Itu sebenarnya adalah deklarasi untuk pengendali event - metode yang akan dipanggil ketika sebuah event dipecat. Untuk membuat acara, Anda akan menulis sesuatu seperti ini:

public class Foo
{
    public event EventHandler MyEvent;
}

Dan kemudian Anda dapat berlangganan ke acara seperti ini:

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

Dengan OnMyEvent () didefinisikan seperti ini:

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

Setiap kali Fooapi mati MyEvent, maka OnMyEventpawang Anda akan dipanggil.

Anda tidak selalu harus menggunakan instance EventArgssebagai parameter kedua. Jika Anda ingin memasukkan informasi tambahan, Anda dapat menggunakan kelas yang berasal dari EventArgs( EventArgsadalah basis dengan konvensi). Misalnya, jika Anda melihat beberapa peristiwa yang ditentukan Controldalam WinForms, atau FrameworkElementdi WPF, Anda dapat melihat contoh peristiwa yang meneruskan informasi tambahan ke penangan peristiwa.


14
Terima kasih telah menjawab pertanyaan dan tidak pergi ke Delegasi dan Acara.
divide_byzero

3
Saya akan merekomendasikan untuk tidak menggunakan OnXXXpola penamaan untuk pengendali acara Anda. (Dengan bodohnya, OnXXX dianggap berarti 'menangani XXX' di MFC, dan 'naikkan XXX' di .net, dan sekarang artinya tidak jelas dan membingungkan - lihat posting ini untuk detailnya ). Nama yang dipilih adalah RaiseXXXuntuk meningkatkan acara, dan HandleXXXatau Sender_XXXuntuk untuk penangan acara.
Jason Williams

1
Bisakah Anda menunjukkan contoh yang berfungsi dengan aplikasi WinForms sederhana?
MC9000

40

Berikut adalah contoh kode yang dapat membantu:

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
Doa delegasi dalam C # 6 dapat disederhanakan menjadi:OnMaximum?.Invoke(this,new MyEventArgs("you've entered..."));
Tim Schmelter

23

Hanya untuk menambah jawaban hebat yang ada di sini - membangun kode pada yang diterima, yang menggunakan delegate void MyEventHandler(string foo)...

Karena kompiler mengetahui tipe delegasi dari acara SomethingHappened , ini:

myObj.SomethingHappened += HandleSomethingHappened;

Sepenuhnya setara dengan:

myObj.SomethingHappened += new MyEventHandler(HandleSomethingHappened);

Dan penangan juga bisa tidak terdaftar dengan -=seperti ini:

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

Demi kelengkapan, meningkatkan acara dapat dilakukan seperti ini, hanya di kelas yang memiliki acara:

//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!");
}

Salinan thread-lokal dari handler diperlukan untuk memastikan doa aman - jika tidak, thread dapat dibatalkan dan membatalkan registrasi handler terakhir untuk acara segera setelah kami memeriksa apakah itu null, dan kami akan bersenang-senang di NullReferenceExceptionsana. .


C # 6 memperkenalkan tangan pendek yang bagus untuk pola ini. Ini menggunakan operator propagasi nol.

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

13

Pemahaman saya tentang peristiwa tersebut adalah;

Melimpahkan:

Variabel untuk menampung referensi ke metode / metode yang akan dieksekusi. Ini memungkinkan untuk menyebarkan metode seperti variabel.

Langkah-langkah untuk membuat dan memanggil acara:

  1. Acara ini adalah turunan dari delegasi

  2. Karena suatu kejadian adalah turunan dari delegasi, maka kita harus terlebih dahulu mendefinisikan delegasi.

  3. Tetapkan metode / metode yang akan dieksekusi ketika acara dipecat ( Memanggil delegasi )

  4. Api acara ( Panggil delegasi )

Contoh:

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

penerbit: di mana peristiwa itu terjadi. Penerbit harus menentukan delegasi mana yang digunakan kelas dan menghasilkan argumen yang diperlukan, meneruskan argumen itu dan dirinya sendiri ke delegasi.

pelanggan: di mana respons terjadi. Pelanggan harus menentukan metode untuk merespons acara. Metode-metode ini harus menggunakan jenis argumen yang sama dengan delegasi. Pelanggan kemudian menambahkan metode ini ke delegasi penerbit.

Oleh karena itu, ketika acara terjadi di penerbit, delegasi akan menerima beberapa argumen acara (data, dll), tetapi penerbit tidak tahu apa yang akan terjadi dengan semua data ini. Pelanggan dapat membuat metode di kelas mereka sendiri untuk merespons acara di kelas penerbit, sehingga pelanggan dapat menanggapi acara penerbit.


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

Saya setuju dengan KE50 kecuali bahwa saya melihat kata kunci 'acara' sebagai alias untuk 'ActionCollection' karena acara tersebut memiliki koleksi tindakan yang harus dilakukan (mis. Delegasi).

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

Jawaban teknis yang luar biasa di pos! Secara teknis saya tidak punya apa-apa untuk ditambahkan.

Salah satu alasan utama mengapa fitur baru muncul dalam bahasa dan perangkat lunak secara umum adalah pemasaran atau politik perusahaan! :-) Ini tidak boleh kurang dari perkiraan!

Saya pikir ini berlaku juga untuk delegasi dan acara tertentu! saya menemukan mereka berguna dan menambah nilai ke bahasa C #, tetapi di sisi lain bahasa Java memutuskan untuk tidak menggunakannya! mereka memutuskan bahwa apa pun yang Anda selesaikan dengan delegasi Anda sudah dapat menyelesaikan dengan fitur yang ada dari bahasa yaitu antarmuka misalnya

Sekarang sekitar tahun 2001 Microsoft merilis .NET framework dan bahasa C # sebagai solusi pesaing untuk Java, jadi ada baiknya untuk memiliki FITUR BARU yang tidak dimiliki Java.


0

Baru-baru ini saya membuat contoh cara menggunakan acara di c #, dan mempostingnya di blog saya. Saya mencoba membuatnya sejelas mungkin, dengan contoh yang sangat sederhana. Jika itu dapat membantu siapa pun, ini dia: http://www.konsfik.com/using-events-in-csharp/

Ini termasuk deskripsi dan kode sumber (dengan banyak komentar), dan terutama berfokus pada penggunaan acara dan penangan acara yang tepat (seperti template).

Beberapa poin utama adalah:

  • Acara seperti "sub-tipe delegasi", hanya lebih terbatas (dengan cara yang baik). Bahkan deklarasi acara selalu menyertakan delegasi (EventHandlers adalah tipe delegasi).

  • Penangan Kejadian adalah tipe khusus delegasi (Anda mungkin menganggapnya sebagai templat), yang memaksa pengguna untuk membuat acara yang memiliki "tanda tangan" tertentu. Tanda tangan dalam format: (pengirim objek, eventArgs eventarguments).

  • Anda dapat membuat sub-kelas Anda sendiri dari EventArgs, untuk memasukkan segala jenis informasi yang perlu disampaikan oleh acara tersebut. Tidak perlu menggunakan EventHandlers saat menggunakan acara. Anda dapat melewati mereka sepenuhnya dan menggunakan jenis delegasi Anda sendiri di tempat mereka.

  • Salah satu perbedaan utama antara menggunakan acara dan delegasi, adalah bahwa peristiwa hanya dapat dipanggil dari dalam kelas tempat mereka dideklarasikan, meskipun mereka dapat dinyatakan sebagai publik. Ini adalah perbedaan yang sangat penting, karena memungkinkan acara Anda diekspos sehingga mereka "terhubung" ke metode eksternal, sementara pada saat yang sama mereka dilindungi dari "penyalahgunaan eksternal".


0

Hal lain yang perlu diketahui tentang , dalam beberapa kasus, Anda harus menggunakan Delegasi / Acara ketika Anda membutuhkan kopling tingkat rendah !

Jika Anda ingin menggunakan komponen di beberapa tempat dalam aplikasi , Anda perlu membuat komponen dengan kopling level rendah dan LOGIC khusus yang tidak dikhawatirkan harus didelegasikan ke luar. komponen Anda! Ini memastikan bahwa Anda memiliki sistem yang dipisahkan dan kode pembersih.

Pada prinsip SOLID ini adalah " D ", ( D prinsip inversi dependensi).

Juga dikenal sebagai " IoC ", Inversion of control .

Anda dapat membuat " IoC " dengan Acara, Delegasi, dan DI (Injeksi Ketergantungan).

Sangat mudah untuk mengakses metode di kelas anak. Tetapi lebih sulit untuk mengakses metode di kelas induk dari anak. Anda harus memberikan referensi orang tua kepada anak tersebut! (atau gunakan DI dengan Antarmuka)

Delegasi / Acara memungkinkan kita untuk berkomunikasi dari anak ke orang tua tanpa referensi!

masukkan deskripsi gambar di sini

Dalam diagram ini di atas, saya tidak menggunakan Delegasi / Peristiwa dan komponen induk B harus memiliki referensi dari komponen induk A untuk menjalankan logika bisnis yang tidak peduli dalam metode A. (kopling tingkat tinggi)

Dengan pendekatan ini, saya harus meletakkan semua referensi dari semua komponen yang menggunakan komponen B! :(

masukkan deskripsi gambar di sini

Dalam diagram ini di atas, saya menggunakan Delegasi / Peristiwa dan komponen B tidak harus diketahui A. (kopling level rendah)

Dan Anda dapat menggunakan komponen B Anda di mana saja di aplikasi Anda !

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.