본문 바로가기

C#

C# 이벤트(Event)

반응형
  • 이벤트를 구독하는 클래스에서 이벤트가 발생했을 때 어떤 처리를 하는 것을 이벤트 핸들러라고 함
  • 이벤트를 구독하기 위해 += 연산자 사용
  • 이벤트 구독을 해제하기 위해 -= 연산자 사용
  • 이벤트를 구독, 등록, 가입, 추가, ... 등등으로 이해
  • 이벤트를 발생시키는 클래스를 Publisher라 하고, 이벤트가 발생하면 처리하는 클래스를 Subscriber라고 함

.NET 지침을 따르는 이벤트를 게시하는 방법

https://docs.microsoft.com/ko-kr/dotnet/csharp/programming-guide/events/how-to-publish-events-that-conform-to-net-framework-guidelines

 

.NET 지침을 따르는 이벤트 게시(C# 프로그래밍 가이드)

.NET 지침을 따르는 이벤트를 게시하는 방법을 알아봅니다. .NET 클래스 라이브러리의 모든 이벤트는 EventHandler 대리자를 기반으로 합니다.

docs.microsoft.com

// 이벤트 정보를 담을 클래스 정의
public class CustomEventArgs
{
    public CustomEventArgs(string message)
    {
        Message = message;
    }

    public string Message { get; set; }
}

// 이벤트를 발행하는 클래스
class Publisher
{
    // 닷넷에서 기본 제공하는 EventHandler<T> 대리자를 사용하여 이벤트 선언
    public event EventHandler<CustomEventArgs> RaiseCustomEvent;

    public void DoSomething()
    {
        // 이벤트 실행
        OnRaiseCustomEvent(new CustomEventArgs("Event triggered"));
    }

    // 하위 클래스에서 재정의 가능한 메서드
    protected virtual void OnRaiseCustomEvent(CustomEventArgs e)
    {
        // 임시 변수에 이벤트 복사(Thread safety 때문)
        EventHandler<CustomEventArgs> raiseEvent = RaiseCustomEvent;

        e.Message += $" at {DateTime.Now}";
        raiseEvent?.Invoke(this, e);
    }
}

// 이벤트 구독 클래스
class Subscriber
{
    private readonly string _id;

    public Subscriber(string id, Publisher pub)
    {
        _id = id;

        // 이벤트 구독
        // pub.RaiseCustomEvent 이벤트에 이벤트 핸들러(HandleCustomEvent 메서드)를 구독
        pub.RaiseCustomEvent += HandleCustomEvent;
    }

    // 이벤트 핸들러(실제 이벤트 처리)
    void HandleCustomEvent(object sender, CustomEventArgs e)
    {
        Console.WriteLine($"{_id} received this message: {e.Message}");
    }
}

 

var pub = new Publisher();
var sub1 = new Subscriber("sub1", pub);
var sub2 = new Subscriber("sub2", pub);

pub.DoSomething();

 


파생 클래스에서 기본 클래스 이벤트를 발생하는 방법

https://docs.microsoft.com/ko-kr/dotnet/csharp/programming-guide/events/how-to-raise-base-class-events-in-derived-classes

 

파생 클래스에서 기본 클래스 이벤트를 발생하는 방법 - C# 프로그래밍 가이드

파생 클래스에서 기본 클래스 이벤트를 생성하는 방법을 알아봅니다. 코드 예제를 살펴보고 사용 가능한 추가 리소스를 확인합니다.

docs.microsoft.com

// 이벤트 정보를 담을 클래스 정의
public class ShapeEventArgs
{
    public ShapeEventArgs(double area)
    {
        NewArea = area;
    }

    public double NewArea { get; }
}

// 이벤트를 발행하는 클래스
public abstract class Shape
{
    protected double _area;

    public double Area
    {
        get => _area;
        set => _area = value;
    }

    public event EventHandler<ShapeEventArgs> ShapeChanged;

    public abstract void Draw();

    protected virtual void OnShapeChanged(ShapeEventArgs e)
    {
        ShapeChanged?.Invoke(this, e);
    }
}

public class Circle : Shape
{
    private double _radius;

    public Circle(double radius)
    {
        _radius = radius;
        _area = 3.14 * _radius * _radius;
    }

    public void Update(double d)
    {
        _radius = d;
        _area = 3.14 * _radius * _radius;
        OnShapeChanged(new ShapeEventArgs(_area));
    }

    protected override void OnShapeChanged(ShapeEventArgs e)
    {
        base.OnShapeChanged(e);
    }

    public override void Draw()
    {
        Console.WriteLine("Drawing a circle");
    }
}

public class Rectangle : Shape
{
    private double _length;
    private double _width;

    public Rectangle(double length, double width)
    {
        _length = length;
        _width = width;
        _area = _length * _width;
    }

    public void Update(double length, double width)
    {
        _length = length;
        _width = width;
        _area = _length * _width;
        OnShapeChanged(new ShapeEventArgs(_area));
    }

    protected override void OnShapeChanged(ShapeEventArgs e)
    {
        base.OnShapeChanged(e);
    }

    public override void Draw()
    {
        Console.WriteLine("Drawing a rectangle");
    }
}

// 이벤트 구독 클래스
public class ShapeContainer
{
    private readonly List<Shape> _list;

    public ShapeContainer()
    {
        _list = new List<Shape>();
    }

    public void AddShape(Shape shape)
    {
        _list.Add(shape);

        // shape.ShapeChanged 이벤트에 이벤트 핸들러(HandleShapeChanged 메서드)를 구독
        shape.ShapeChanged += HandleShapeChanged;
    }

    private void HandleShapeChanged(object sender, ShapeEventArgs e)
    {
        if (sender is Shape shape)
        {
            Console.WriteLine($"Received event. Shape area is now {e.NewArea}");

            shape.Draw();
        }
    }
}

 

var circle = new Circle(54);
var rectangle = new Rectangle(12, 9);
var container = new ShapeContainer();

container.AddShape(circle);
container.AddShape(rectangle);

circle.Update(57);
rectangle.Update(7, 7);

 

  1. Circle 클래스와 Rectangle 클래스는 Shape 클래스를 상속받고 있음
  2. circle 인스턴스 생성, container 인스턴스 생성(shape 들을 관리)
  3. container 인스턴스의 AddShape(circle) 메서드 호출 circle 인스턴스의 Shape.ShapeChanged 이벤트 구독
  4. circle 인스턴스의 Update 메서드 호출
    1. circle 인스턴스의 OnShapeChanged 메서드 호출
    2. circle 인스턴스의 Shape.OnShapeChanged 메서드 호출
    3. Shape.ShapeChanged 이벤트 실행
    4. Shape.ShapeChanged 이벤트에 구독한 이벤트 핸들러 호출
    5. container 인스턴스의 HandleShapeChanged 메서드(구독한 이벤트 핸들러) 호출
    6. 결과 출력

인터페이스 이벤트를 구현하는 방법

https://docs.microsoft.com/ko-kr/dotnet/csharp/programming-guide/events/how-to-implement-interface-events

 

인터페이스 이벤트를 구현하는 방법 - C# 프로그래밍 가이드

클래스에서 인터페이스 이벤트를 구현하는 방법을 알아봅니다. 코드 예제를 살펴보고 사용 가능한 추가 리소스를 확인합니다.

docs.microsoft.com

public interface IDrawingObject
{
    // 객체를 그리기 전에 이 이벤트를 발생시킴
    event EventHandler OnDraw;
}
public interface IShape
{
    // 객체를 그린 후에 이 이벤트를 발생시킴
    event EventHandler OnDraw;
}

// 이벤트를 발행하는 클래스
public class Shape : IDrawingObject, IShape
{
    event EventHandler PreDrawEvent;
    event EventHandler PostDrawEvent;

    object objectLock = new Object();

    event EventHandler IDrawingObject.OnDraw
    {
        add
        {
            lock (objectLock)
            {
                PreDrawEvent += value;
            }
        }
        remove
        {
            lock (objectLock)
            {
                PreDrawEvent -= value;
            }
        }
    }

    event EventHandler IShape.OnDraw
    {
        add
        {
            lock (objectLock)
            {
                PostDrawEvent += value;
            }
        }
        remove
        {
            lock (objectLock)
            {
                PostDrawEvent -= value;
            }
        }
    }

    public void Draw()
    {
        // 객체가 그려지기 전에 IDrawingObject의 이벤트 발생
        PreDrawEvent?.Invoke(this, EventArgs.Empty);

        Console.WriteLine("Drawing a shape.");

        // 객체가 그려진 후 IShape의 이벤트 발생
        PostDrawEvent?.Invoke(this, EventArgs.Empty);
    }
}

// IDrawingObject 참조
public class Subscriber1
{
    public Subscriber1(Shape shape)
    {
        IDrawingObject d = (IDrawingObject)shape;
        // IDrawingObject.OnDraw 이벤트에 이벤트 핸들러(d_OnDraw 메서드)를 구독
        d.OnDraw += d_OnDraw;
    }

    void d_OnDraw(object sender, EventArgs e)
    {
        Console.WriteLine("Sub1 receives the IDrawingObject event.");
    }
}

// IShape 참조
public class Subscriber2
{
    public Subscriber2(Shape shape)
    {
        IShape d = (IShape)shape;
        // IShape.OnDraw 이벤트에 이벤트 핸들러(d_OnDraw 메서드)를 구독
        d.OnDraw += d_OnDraw;
    }

    void d_OnDraw(object sender, EventArgs e)
    {
        Console.WriteLine("Sub2 receives the IShape event.");
    }
}

 

Shape shape = new Shape();
Subscriber1 sub = new Subscriber1(shape);
Subscriber2 sub2 = new Subscriber2(shape);
shape.Draw();

반응형

'C#' 카테고리의 다른 글

C# asyn, await  (0) 2021.12.27
C# 확장 메서드  (0) 2021.12.27
C# 대리자(Delegate), 무명 메서드, 람다식  (0) 2021.12.26
C# 제네릭(Generic)  (0) 2021.12.24
C# string을 int로 변환 TryParse  (0) 2021.12.24