C #의 이벤트 및 이벤트 핸들러 이해


329

특히 사용자 인터페이스를 만드는 맥락에서 이벤트의 목적을 이해합니다. 이것이 이벤트를 만들기위한 프로토 타입이라고 생각합니다.

public void EventName(object sender, EventArgs e);

이벤트 핸들러는 무엇을하고 왜 필요하며 어떻게 만들 수 있습니까?


9
@Andy가 지적한 것처럼 여기에서 코드 스 니펫은 이벤트 자체가 아니라 이벤트에 등록 된 메소드를 설명합니다.
dthrasher


답변:


660

이벤트 핸들러를 이해하려면 델리게이트 를 이해해야 합니다. C # 에서는 대리자를 메서드에 대한 포인터 (또는 참조)로 생각할 수 있습니다. 포인터를 값으로 전달할 수 있기 때문에 유용합니다.

대리인의 중심 개념은 서명 또는 형태입니다. 즉 (1) 반환 유형과 (2) 입력 인수입니다. 우리가 대리자를 만드는 경우 예를 들어 void MyDelegate(object sender, EventArgs e), 그것은 단지 어느 반환 방법을 가리킬 수 있습니다 void및을 object하고 EventArgs. 사각형 구멍과 사각형 못과 같은 종류. 그래서 우리는이 메소드들이 델리게이트와 동일한 서명 또는 형태를 가지고 있다고 말합니다.

메소드에 대한 참조를 작성하는 방법을 알고 이벤트의 목적에 대해 생각해 보자. 시스템의 다른 곳에서 발생하거나 "이벤트를 처리"할 때 일부 코드가 실행되도록하고 싶다. 이를 위해 실행하려는 코드에 대한 특정 메소드를 만듭니다. 이벤트와 실행될 메소드 사이의 아교는 대리인입니다. 이벤트는 이벤트가 발생할 때 호출 할 메소드에 대한 포인터 목록을 내부적으로 저장해야합니다. * 물론 메소드를 호출하려면 전달할 인수를 알아야합니다! 우리는 델리게이트를 이벤트와 호출 될 모든 특정 메소드 간의 "계약"으로 사용합니다.

따라서 기본값 EventHandler(및 그와 비슷한 유형)은 특정 형태의 메소드를 나타냅니다 (다시 말하면 void / 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에서 이벤트의 핵심이며 "마법"을 제거합니다. 이벤트는 커버 아래에 같은 "모양"의 메소드 목록 일뿐입니다.이 목록은 이벤트가있는 곳에 저장됩니다. 이벤트가 발생하면 실제로는 "이 메소드 목록을 살펴보고이 값을 매개 변수로 사용하여 각 메소드를 호출합니다."이벤트 핸들러를 지정하는 것은이 메소드 목록에 메소드를 추가하는 더 예쁘고 쉬운 방법입니다. 호출).


24
이제 누구나 이벤트가 EventHandler라고하는 이유를 설명 할 수 있습니까? 혼란스러운 모든 명명 규칙 중에서 이것은 최악의 상황입니다.
Joel in Gö

37
@Joel in Go 이벤트는 EventHandler라고 부릅니다. EventHandler는 이벤트가 이벤트와 통신하는 모든 사람과 맺어야하는 계약입니다. "string MyString"과 같습니다. 문자열이 유형을 선언합니다. event MyEventHandler이 이벤트는이 이벤트와 상호 작용하는 모든 사람이 MyEventHandler 계약을 준수해야한다고 선언합니다. 처리기 규칙은 계약에 주로 이벤트 처리 방법이 설명되어 있기 때문입니다.
Rex M

18
행사는 어떻게 시작 되나요?
alchemical

17
@Rex M : 내가 본 "MyEventHandler"에 대한 첫 번째 일관된 설명에 감사합니다. :)
Joel in 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()하나 개의 중앙 위치 (일명에서 팩토리 메소드 패턴 ).

팩토리 메소드 패턴

따라서 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
   }

}

따라서 유형을 노출시키지 않고 다른 클래스에 대리자를 사용할 수 있습니다. 노출하는 유일한 방법은 메소드의 서명입니다 (당신이 가진 매개 변수 수 등).

"나의 방법의 서명", 전에는 어디서 들었습니까? 예, 인터페이스 !!! 인터페이스는 전체 클래스의 서명을 설명합니다. 델리게이트는 한 가지 방법의 서명 만 설명하는 것으로 생각하십시오!

인터페이스와 델리게이트의 또 다른 큰 차이점은 클래스를 작성할 때 "이 메소드는 해당 유형의 델리게이트를 구현합니다"라고 말할 필요가 없다는 것입니다. 인터페이스를 사용하면 "이 클래스는 해당 유형의 인터페이스를 구현합니다"라고 말해야합니다.

또한 대리자 참조는 (일부 제한 사항이 있지만 아래 참조) 여러 메서드 ()를 참조 할 수 있습니다 MulticastDelegate. 이것은 델리게이트를 호출 할 때 명시 적으로 연결된 여러 메소드가 실행됨을 의미합니다. 객체 참조는 항상 하나의 객체 만 참조 할 수 있습니다.

A에 대한 제한은 MulticastDelegate제 (방법 / 대리인) 서명이있는 반환 값 (이없는 것이 있습니다 void) 및 키워드 outref서명에 사용되지 않습니다. 분명히 숫자를 반환하고 같은 숫자를 반환 할 것으로 예상되는 두 가지 메서드를 호출 할 수 없습니다. 서명이 준수되면 대리인은 자동으로 MulticastDelegate입니다.

행사

이벤트는 다른 객체에서 델리게이트에 대한 구독을 표시하는 속성 (예 : get; set; 속성을 인스턴스 필드로)과 같습니다. 그러나 이러한 속성은 get; set;을 지원하지 않습니다. 대신에 그들은 add를 지원합니다; 없애다;

그래서 당신은 가질 수 있습니다 :

    Action myField;

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

UI에서의 사용법 (WinForms, WPF, UWP 등)

이제 델리게이트는 메소드에 대한 참조이며, 델리게이트에서 참조 할 메소드를 제공 할 수 있고 UI 버튼이라는 것을 전세계에 알리는 이벤트를 가질 수 있습니다. 클릭 여부에 관심이있는 모든 사람에게 (우리가 노출 된 이벤트를 통해) 당사에 방법을 등록하도록 요청할 수 있습니다. 우리는 우리에게 주어진 모든 방법을 사용하고 대리인이 참조 할 수 있습니다. 그리고 기다렸다가 기다립니다 .... 사용자가 와서 해당 버튼을 클릭 할 때까지 델리게이트를 호출 할 충분한 이유가 있습니다. 그리고 대리인은 우리에게 주어진 모든 방법을 참조하기 때문에 모든 방법이 호출됩니다. 우리는 그 메소드가 무엇을하는지, 어떤 클래스가 그 메소드를 구현하는지 모릅니다. 우리가 관심을 갖는 것은 누군가 클릭에 관심이 있다는 것입니다.

자바

Java와 같은 언어에는 대리자가 없습니다. 대신 인터페이스를 사용합니다. 그들이하는 방법은 '우리의 클릭'에 관심이있는 사람에게 특정 인터페이스 (우리가 호출 할 수있는 특정 방법으로)를 구현하도록 요청한 다음 인터페이스를 구현하는 전체 인스턴스를 제공하는 것입니다. 우리는이 인터페이스를 구현하는 모든 객체의 목록을 유지하며 클릭 할 때마다 '호출 할 수있는 확실한 메소드'를 호출 할 수 있습니다.


설명을 응원하지만 이벤트가 가입자를 대신하는 대리인의 인스턴스와 어떻게 다릅니 까? 둘 다 정확히 같은 것 같아요?
BKSpurgeon

@BKSpurgeon "구독자를 받아들이는 대의원" 이기 때문에 event구문 설탕 일뿐입니다.
Mathieu Guindon 2016 년

"MulticastDelegate의 제한 사항은 (메소드 / 델리게이트) 서명에 반환 값 (void)이 없어야한다는 것입니다."이것이 올바른 것으로 생각하지 않습니다. 반환 값이 있으면 마지막 값을 반환합니다.
Hozikimaru

"따라서 타입을 노출시키지 않고 다른 클래스에 사용할 델리게이트를 줄 수 있습니다. 노출하는 유일한 방법은 메소드의 서명입니다 ..." -나에게 중요한 포인트입니다. 감사합니다!
라이언

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규칙은 기본입니다). 예를 들어, ControlWinForms 또는 FrameworkElementWPF 에 정의 된 일부 이벤트를 보면 추가 정보를 이벤트 핸들러에 전달하는 이벤트의 예를 볼 수 있습니다.


14
질문에 답변하고 대표단과 행사에 가지 말아 주셔서 감사합니다.
divide_byzero

3
OnXXX이벤트 핸들러에 네이밍 패턴을 사용하지 않는 것이 좋습니다 . (OnXXX는 MFC에서 'handle XXX', .net에서 'raise XXX'를 의미하므로 이제 그 의미가 불분명하고 혼동 됩니다. 자세한 내용은이 게시물을 참조하십시오 ). 선호되는 이름은 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

이벤트에 수행 할 작업 모음 (예 : 대리인)이 있으므로 'event'키워드를 'ActionCollection'의 별칭으로 간주한다는 점을 제외하고는 KE50에 동의합니다.

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 언어는 그것들을 사용하지 않기로 결정했습니다! 그들은 당신이 델리게이트로 해결하고있는 모든 것이 당신이 이미 언어의 기존 기능, 즉 인터페이스로 해결할 수 있다고 결정했습니다

이제 2001 년경 Microsoft는 Java에 대한 경쟁 업체 솔루션으로 .NET 프레임 워크 및 C # 언어를 출시 했으므로 Java에없는 새로운 기능을 갖추는 것이 좋습니다.


0

최근에 C #에서 이벤트를 사용하는 방법에 대한 예를 만들어 내 블로그에 게시했습니다. 아주 간단한 예를 들어 가능한 한 명확하게 만들려고 노력했습니다. 누군가를 도울 수있는 경우 여기에 있습니다 : http://www.konsfik.com/using-events-in-csharp/

여기에는 설명과 소스 코드 (많은 주석 포함)가 포함되며 주로 이벤트 및 이벤트 핸들러의 적절한 (템플릿과 같은) 사용법에 중점을 둡니다.

몇 가지 핵심 사항은 다음과 같습니다.

  • 이벤트는 "하위 유형의 델리게이트"와 유사하며 더 제한적입니다 (좋은 방법으로). 실제로 이벤트 선언에는 항상 델리게이트가 포함됩니다 (EventHandlers는 델리게이트 유형입니다).

  • 이벤트 핸들러는 특정 유형의 델리게이트 (템플릿으로 생각할 수 있음)로, 사용자가 특정 "서명"이있는 이벤트를 작성하도록합니다. 서명의 형식은 다음과 같습니다 (개체 전송자, EventArgs eventarguments).

  • 이벤트가 전달하는 데 필요한 모든 유형의 정보를 포함하기 위해 고유 한 하위 클래스의 EventArgs를 작성할 수 있습니다. 이벤트를 사용할 때 EventHandlers를 사용할 필요는 없습니다. 당신은 그것들을 완전히 건너 뛰고 대신에 당신 자신의 종류의 대리인을 사용할 수 있습니다.

  • 이벤트와 델리게이트 사용의 한 가지 주요 차이점은 이벤트는 공개로 선언 된 경우에도 선언 된 클래스 내에서만 이벤트를 호출 할 수 있다는 것입니다. 이는 이벤트가 외부 메소드에 "연결되어"동시에 "외부 오용"으로부터 보호 될 수 있도록하기 때문에 매우 중요한 차이점입니다.


0

알아야 할 또 다른 사항은 경우에 따라 낮은 수준의 커플 링 이 필요할 때 대의원 / 이벤트를 사용해야합니다 !

응용 프로그램에서 여러 곳에서 구성 요소사용하려면 결합 수준이 낮은 구성 요소를 만들어야하며 구성되지 않은 특정 LOGIC을 구성 요소 외부 에서 위임해야합니다 ! 이렇게하면 분리 된 시스템과 더 깨끗한 코드가 보장됩니다.

에서 SOLID 원칙이는 "입니다 D "( D의 ependency 반전 원칙).

" IoC " 라고도하며 제어 반전 .

이벤트, 대리인 및 DI (종속성 주입)를 사용하여 " IoC "를 만들 수 있습니다 .

자식 클래스의 메서드에 쉽게 액세스 할 수 있습니다. 그러나 자식 클래스에서 부모 클래스의 메서드에 액세스하기가 더 어렵습니다. 부모 참조를 자식에게 전달해야합니다! (또는 인터페이스와 함께 DI 사용)

대표자 / 행사를 통해 우리는 참조없이 자녀와 부모 사이에 의사 소통을 할 수 있습니다!

여기에 이미지 설명을 입력하십시오

위의 다이어그램에서 위임 / 이벤트를 사용하지 않으며 부모 구성 요소 B A의 방법으로 원치 않는 비즈니스 로직을 실행하기 위해 부모 구성 요소 A에 대한 참조를 가져야합니다 . (높은 수준의 커플 링)

이 접근법을 사용하면 구성 요소 B를 사용하는 모든 구성 요소의 모든 참조를 넣어야합니다! :(

여기에 이미지 설명을 입력하십시오

위의 다이어그램에서 Delegate / Event 를 사용하고 구성 요소 B는 A를 알 필요가 없습니다. (낮은 수준의 커플 링)

그리고 어플리케이션의 어느 곳에서나 컴포넌트 B를 사용할 수 있습니다 !

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.