몇가지 주제로 더 살펴보자.

냐옹·2024년 4월 9일
0

.NET

목록 보기
4/31

몇가지 주제로 더 살펴보자.

첫번째 주제는 구조체와 클래스이다. 일반적으로는 구조체는 함수가 없고 변수만 들어갈 수 있다고 알 고 있지만, 구조체 안에도 함수가 들어갈 수 있다. 그렇다하면 클래스와 구조체가 어떤 점에서 차이가 있을까.
먼저 겉으로 보았을 때 별로 차이가 없다. 클래스와 마찬가지로 구조체도 필드로 변수와 메서드를 가질 수 있으며 생성자 역시 가질 수 있다. 하지만 큰 차이가 2가지 존재한다.
1. 구조체는 상속이 불가능하고
2. 클래스 객체는 힙에 할당되지만, 구조체 객체는 스택에 할당된다.
A. 힙은 GC가 돌아가지만 스택은 아니다. 따라서 성능상으로는 구조체가 유리하긴하다.
3. 하지만 구조체로 선언을 했어도 객체를 힙 영역에 메모리를 할당할 때가 있다.
A. 모든 필드의 합이 16바이트를 넘는 경우
B. 구조체 안에 클래스 타입을 필드로 가질 경우

두번째 주제는 sealed이다. 함수 선언 시에 sealed 키워드를 붙이면 상속할 때 해당 함수를 오버라이딩 할 수 없다. 대표적으로 string 클래스가 sealed 클래스이다. 이건 주제라고 하기도 뭐하다.

세번째 주제는 대리자 (Delegate)이다. 대리자를 사용하면 메서드를 변수처럼 전달하고 호출할 수 있다. 먼저 대리자는 다음과 같은 상황에서 쓸모가 있다.
1. 이벤트 처리
A. 이벤트와 함께 대리자를 사용하여 이벤트 핸들러를 등록하고 호출할 수 있다.
2. 비동기 프로그래밍
3. 콜백 메서드
난 개인적으로 대리자가 너무 이해가 안된다. 이게 참조자 형태 같기는 한데, 왜이렇게 이해가 안되지.
namespace ConsoleApp3{
public delegate void PrintDelegate(string message);
internal class Program
{
public static void Main(string[] args)
{
PrintDelegate print = PrintMessage;

      print.Invoke("suwon");
      print("suwon");
  }

  public static void PrintMessage(string message)
  {
      Console.WriteLine(message);
  }

}}
진짜 하나하나 다 뜯어보자. 먼저 맨 위부터 보자.
첫번째로 왜 Delegate 선언이 클래스 안에 속해있지 않은건지가 궁금할 수가 있다. 이유는 이들이 네임스페이스 레벨에서 선언되기 때문이다. 구조체와 대리자는 형(타입)이다. 프로그램의 여러부분에서 재사용될 수 있도록 저기에 선언한다.

  • 네임스페이스 레벨의 타입 선언
    c#에서 클래스, 구조체, 인터페이스, 열거형, 그리고 대리자는 네임스페이스 레벨에서 선언될 수 있다. 이것의 목적은 해당 타입들이 네임스페이스 안에서 전역적으로 접근가능하게 하여, 다른 클래스나 구조체, 그리고 다른 타입들 사이에서 재사용될 수 있게 함이다.

  • 클래스 내부선언
    반면, 내부 클래스나 메서드, 속성 등은 특정 클래스의 내부에서만 선언된다. 이들은 주로 해당 클래스의 구현 세부 사항을 돕기 위해서 존재하며, 외부에서는 직접적으로 사용되지 않는 경우가 많다.

  • 구조체와 대리자의 재사용성
    구조체는 값 타입을 정의하며, 주로 작은 데이터 그룹을 효율적으로 캡슐화 하기 위해서 사용된다. 구조체는 프로그램의 여러 부분에서 데이터를 전달하거나 처리할 때 유용하게 사용된다.
    대리자! 는 메서드에 대한 참조를 보유하는 타입으로, 이벤트 핸들링, 콜백함수 구현 등에 사용된다. 대리자는 프로그램 전반에 걸쳐서 재사용될 수 있는 메서드 시그니쳐를 정의한다. 위의 형광펜 쳐진 코드가 메서드 시그니쳐이다.

  • 메서드 시그니쳐
    메서드 시그니쳐는 메서드를 고유하게 식별하는데에 사용되는 정보의 조합으로 메서드의 이름 / 매개변수 리스트 ( 매개변수의 타입, 순서, 개수)를 포함한다. 반환 타입은 매서드 시그니쳐의 일부로 간주되지 않는다. 즉 같은 이름과 매개변수 리스트를 가지지만 반환 타입만 다른 메서드는 오버로드 할 수 없다. ( 아~ )
    메서드 시그니쳐는 오버로딩, 즉 같은 이름을 가진 여러 메서드를 한 클래스 내에 정의할 때 중요한 역할을 한다. 오버 로딩된 메서드는 서로 다른 매개변수 리스트 즉 서로 다른 메서드 시그니쳐를 가져야 한다.
    아래는 메서드 시그니쳐의 예시이다.
    public int Add(int a, int b)
    여기에서 메서드 시그니쳐는 Add(int, int)이다.

  • 대리자 – Invoke, BeginInvoke, EndInvoke
    C#에서 대리자는 메서드의 참조를 보유할 수 있는 타입이라고 했다. 대리자를 사용하여 메서드를 변수에 할당하고, 다른 메서드의 매개변수로 전달하거나, 반환 값으로 사용할 수 있다.
    대리자와 관련된 것으로 Invoke, BeginInvoke, EndInvoke가 있는데 다음과 같은 역할을 한다.

  • 먼저 Invoke를 보자
    Invoke 메서드는 대리자가 참조하는 메서드를 동기적으로 호출한다. 이것은 대리자 인스턴스를 통해서 메서드를 직접 호출하는 것과 동일하다. 그러니까
    예를 들어서
    public delegate void PrintDelegate(string message)가 있다고 해보자.
    이걸 PrintDelgate.Invoke(“message”); 로 호출할수도 있으나 PrintDelegate(“message”); 할 수도 있다는 것이다. 근데 후자가 더 잘 쓰인다고 한다. 그래서 Invoke() 메서드는 거의 잘 안쓰일수도
    하지만

  • BeginInvoke는 필요할 수 있다. EndInvoke도 마찬가지이다.
    BeginInvoke 메서드는 대리자가 참조하는 메서드를 비동기적으로 호출한다. 이는 닷넷의 비동기 프로그래밍 모델(APM, asynchronous Programming Model)의 일부로 메서드 실행을 시작하고 즉시 제어를 호출자에게 반환한다.
    BeginInvoke는 일반적으로 비동기 작업의 시작점에 사용되며, 콜백 메서드와 AsyncCallback 대리자를 통해서 비동기 작업의 완료를 처리할 수 있다. 사용 예시는 다음과 같다.
    myDelegate.BeginInvoke(“Hello world”, new AsyncCallback(MyCallBack), null);
    여기서 MyCallBack은 비동기 작업이 완료되었을 때 호출되는 메서드이다.

  • EndInvoke
    EndInvoke 메서드는 BeginInvoke에 의해 시작된 비동기 작업의 완료를 처리한다. 이 메서드는 비동기 작업의 결과를 검색하고 필요한 경우에 예외처리를 수행한다. EndInvoke는 비동기 작업이 완료될 때까지 블록되며, 작업의 결과를 반환하거나 예외를 던진다.
    EndInvoke를 사용하는 일반적인 패턴은 다음과 같다.
    result = myDelegate.EndInvoke(asyncResult);
    여기서 asyncResult는 BeginInvoke 호출에서 반환된 IAsyncResult 객체이다.

  • 아...
    이제는 Invoke, BeginInvoke, EndInvoke 사용이 권장되지 않고, Task, async, await을 사용한 비동기 프로그래밍 모델이 권장된다고 한다..

namespace ConsoleApp5
{
public delegate bool MyDelegate(string message);
internal class Program
{
static public bool MyHandler(string message)
{
Console.WriteLine(message);
return true;
}

    static void Main(string[] args)
    {
        Raiser raiser = new Raiser();
        raiser.MyEvent += new MyDelegate(MyHandler);

        raiser.ActRaiseEvent(10);

    }
}

public class Raiser
{
    public event MyDelegate MyEvent;

    public void ActRaiseEvent(int num)
    {

        for (int i = 0; i < num; i++)
        {
            if (i % 2 == 0)
            {
                Console.WriteLine(MyEvent("raised Event"));
                Console.Write("\n");
            }
            else
            {
                Console.WriteLine(i);
            }
        }
    }
}

}

0개의 댓글