Delegate

  • C#에서 메서드를 대신 참조할 수 있는 형식으로, 메서드를 변수처럼 사용할 수 있게 해 준다
  • 사용자가 직접 정의하여 반환 형식과 매개변수를 지정 가능
  • Delegate는 Action과 Func의 기본이 되는 개념

Action

  • 반환 값이 없는(void) 메서드를 참조하는 제네릭 대리자
  • 최대 16개까지 입력 매개변수를 가질 수 있음
  • Action<T1, T2, ...>와 같은 형태로 사용됨

Func

  • 반환 값이 있는 메서드를 참조하는 제네릭 대리자
  • 마지막 타입 매개변수가 항상 반환 타입을 나타냄
  • Func<T1, T2, ..., TResult>와 같은 형태로 사용되며, 최대 16개의 입력 매개변수를 가질 수 있음

(+) microsoft c# 공식문서에 가면 16개의 매개변수를 가지는 Action과 Func 예시를 볼 수 있다.


델리게이트 설명과 예시

2025.02.10 - [c#] - [c#]delegate 이해하기

 

[c#]delegate 이해하기

1. delegate    □ 개념    □ 예시📝 delegate (대리자)1. 개념□ 메서드에 대한 참조를 나타내는 형식 □ C++의 함수 포인터와 비슷하게 동작함!           ■함수포인터?           : 함수의 시

youcheachae.tistory.com

 

📝함수호출                                                                               

: 함수가 인수 (매개변수)를 처리하는 방식에는 두 가지가 있다.

: 첫 번째 값에 의한 호출 (call by value) , 두 번째로 참조에 의한 호출 (call by reference)

 

📝call by value                                                                       

값에 의한 호출

  • 함수에 변수를 전달할 때 값을 복사해서 선달함
  • 함수 내부에서 값을 변경해도 원래의 변수에 영향을 미치지 않는다
  • int, float, string, bool 등의 원시 타입은 값 복사

원시타입을 매개변수로 가지는 예시를 보겠다

static void Main(string[] args)
{
    Console.WriteLine("Hello, World!");

    int a = 10;
    int b = 11;
    CallbyValue(a, b);
}

public static void CallbyValue(int tempA, int tempB) 
{
    tempA++;
    tempB++;
}

  함수에서 매개변수로 들어온 int값이 바뀌었지만, 원래 값은 변경되지 않는다.

 

  main함수가 실행될 때 스택 메모리에 a와 b 변수가 선언된다

CallByValue 메서드가 호출되면 스택에 tempA와 tempB가 할당되고 , a와 b의 값을 복사한다

함수 실행이 종료되면 tempA와 tempB는 스택에서 해제된다

즉, 함수 내에서 복사한 값을 변경하였기 때문에 원본 값은 변경되지 않는다

 

📝call by reference                                                                

참조에 의한 호출

  • 함수에 변수를 전달할 때 값을 참조, 즉 메모리 주소를 전달
  • 원래의 변수는 함수 외부에서도 변경된 값을 유지한다
  • 객체, 배열 같은 참조타입은 참조값(주소)이 복사되어 함수에 전달

클래스를 참조값으로 가지는 예시를 보겠다

 static void Main(string[] args)
 {
     Console.WriteLine("Hello, World!");
     
     Apple test = new Apple();
     CallbyReference(test);
 } 
 
 public static void CallbyReference(Apple test) 
 {
     test.count += 10;
 }
 
 public class Apple
{
    public int count = 1;
}

  함수 내에서 Apple클래스의 count값이 변경되었고, 원래 객체에도 변경이 반영된다

 

  Apple클래스를 인스턴스와 하면 힙 메모리에 할당된다

  apple 변수는 Apple 인스턴스의 찹조값(주소)을 가진다.

CallbyReference 메서드에 참조값(주소)을 전달하면, 해당 주소값이 복사되어 스택에 저장된다.

함수 내에서는 참조값(주소)에 해당하는 객체에 접근하여 값을 변경시킨다

즉, 참조된 객체의 속성을 변경하였기 때문에 원래 객체에도 변경이 반영된다

 

📝공통점 / 차이점                                                                       

  공통점

  • 모두 매개변수로 전달된 값 (값 자체 또는 참조값)을 복사한다
  • 복사된 값은 스택에 저장되고 함수 종료 시 해제된다 (스택프레임과 연관 있음)

  차이점

  • Call by Value : 원시타입 (int 등)의 값 자체가 복사된다
  • Call by Reference : 객체의 참조 주소값이 복사된다

 

(+) 스택프레임

함수가 호출될 때, 스택 영역에 차례대로 저장되는 함수의 호출정보이다.

함수가 호출될 때, 매개변수를 스택에 할당하기 때문에 두 방식 다 매개변수로 전달된 값을 복사한다.

( 참조타입의 경우 객체 자체는 힙에 있고, 참조만 스택에 복사된다! )

또한 함수의 호출이 끝나면 스택메모리에서 해제되기 때문에 복사된 값은 스택에서 해제된다.

 


스택프레임에 대한 자세한 설명은 아래 링크를 참고

 

2025.02.13 - [용어정리] - [용어정리]스택 프레임 (stack frame)

 

[용어정리]스택 프레임 (stack frame)

🔖 메모리 구조에 대해서 잘 모르시는 분들은 이전 글을 참고해 주세요!  2025.02.12 - [c#] - [c#]힙메모리와 스택 메모리 [c#]힙메모리와 스택 메모리메모리의 공간 1. 코드 (code) 영역2. 데이터 (data)

youcheachae.tistory.com

 

static 키워드를 사용하는 객체 처음 사용될 때 한번 할당되고, 프로그램이 끝날 때까지 메모리를 유지한다. 사용할 때 같은 메모리를 참조한다. 즉 정적이다.

반대로 동적인 객체는 사용할 때 마다 메모리를 사용하고, 사용이 끝나면 메모리를 해제한다. 

 

📝 정적 변수

static void Main(string[] args)
{
    Console.WriteLine("Hello, World!");

    // 정적변수
    // 클래스명.변수 < 로 접근
    Cinema.person += 1;

    Cinema cinema = new Cinema();
    
    // 오류 : 인스턴스화 하면 정적 변수가 포함되지 않는다
    // 정적 멤버는 클래스에 속하는 것이지 인스턴스에 속하는 것이 아님
    cinema.person
}

public class Cinema
{
    // 정적 변수
    public static int person = 0;

    public Cinema() { }
}

□ 정적으로 선언된 변수는 클래스명.변수명 으로 접근할 수 있다.

정적 변수가 포함된 클래스를 인스턴스화 하면 정적 변수는 포함되지 않는다.

 

📝 정적 클래스

static void Main(string[] args)
{
    Console.WriteLine("Hello, World!");
    
    // 정적 클래스
    Animal.animalAge += 1;
    Animal.Setting();
}

public static class Animal 
{
    // 오류 : 정적 클래스에 인스턴스 변수를 선언할 수 없다
    public int age = 0;

    // 오류 : 정적 클래스에 인스턴스 메서드를 선언할 수 없다
    public void Func() { }

    // 정적 변수 
    public static int animalAge = 5;
    
    // 정적 메서드
    public static void Setting() { }
}

정적으로 선언한 클래스의 필드와 메서드는 클래스명.변수명/ 클래스명. 함수명으로 접근할 수 있다.

 

🔖 정적선언을 할 때 주의해야할 점

1. 정적 클래스는 인스턴스화 할 수 없다.

2. 정적인 클래스는 정적인 변수와 메서드만 가질 수 있다.

 

📝 정적 메서드 

static void Main(string[] args)
{
    Console.WriteLine("Hello, World!");

    // 정적 메서드
    Food.Eat();
    Food temp = new Food();
    
    // 오류 : 인스턴스화 하면 정적 메서드는 포함되지 않는다
    temp.Eat()
}


public class Food 
{
    static public void Eat() { }
}

 

□ 정적으로 선언된 함수는 클래스명.변수명 으로 접근할 수 있다.

 정적 메서드가 포함된 클래스를 인스턴스화 하면 정적 변수는 포함되지 않는다.

1. 설계 
2. 트리구조와 무슨 연관? 
3. 퀘스트 수락 -> 퀘스트 완료 -> 연계 퀘스트 추가 플로우
4. 구현 코드
5. 마무리

 

📝 1. 퀘스트 시스템

🔖 설계

 

QuestState 

          ■ 퀘스트의 상태를 나타내기 위해 만든 enum  

          ■ 받기전, 받은 후, 보상 수령 가능한 상태, 수령받은 후 끝난 상태

QuestClass 클래스

           abstract 클래스

           '퀘스트 완료 조건 메서드'를 저장하는 Func<bool> completeQuest 를 필드로 가지고 있음 

           TodoMission() 메서드

                       하위클래스에서 '퀘스트 완료 조건' 구현 

                        조건이 만족하면 => true 반환

                        조건이 불만족하면 => false 반환

 MonsterKillQuest 클래스 / EquiptQuest 클래스

           TodoMission () 메서드를 오버라이딩 

QuestManager 클래스

           List<Quest> performedQuest 

                        수행가능한 퀘스트 클래스들을 담을 컨테이너 

 

📝 2. 트리랑 무슨 연관?

: 연계퀘스트를 어떻게 구성할지 다이어그램을 작성하다가 이 구조가 트리와 굉장히 닮았다고 생각했다.

: 퀘스트에서 트리구조를 사용하면 몇 가지 장점이 있다.

1. 부모퀘스트는 자식에 해당하는 퀘스트들을 관리할 수 있다.

2. 자식에 해당하는 퀘스트는 부모퀘스트 정보를 알 수 있다.

3. 자식에 해당하는 퀘스트는 부모퀘스트를 트리에서 탐색(search) 할 수 있다.

 

🔖 그냥 리스트 사용하면 안 되나?

그럼 해당 퀘스트가 어떤 퀘스트로부터 파생되었는지 알기 힘들다.

 

예를 들어서, 수행하지 못하는 퀘스트도 화면에 표시한다고 생각하자

------------------

[퀘스트]

1. Armor 장비 한 개 착용                 [완료가능]

2. Weapon 장비 한개 착용             [수락가능]

3. 인벤토리에 15개 이상 장비 착용  [수행불가]

------------------

이미지상 '인벤토리에 15개 이상 장비 착용'퀘스트는 '무기 장비 1개 착용'퀘스트 후에 수행가능한 연계퀘스트이다.

즉 , '무기 장비 1개 착용'을 완료해야 퀘스트 수락이 가능하다.

이때, ' 인벤토리에 15개 이상 장비 착용' 퀘스트가 어느 퀘스트로부터 파생되었는지 알 수 있는 방법이 없다.

그래서 트리 구조로 [부모퀘스트] [자식퀘스트]를 연결시키는 것이다.

 

📝 3. 수락 완료  연계퀘스트 추가 플로우

: 주의 깊게 봐야 할 부분은 빨간색 박스 부분이다.

: 퀘스트를 완료했다면 

→ 완료한 퀘스트의 연관퀘스트를 '수행가능한 퀘스트 리스트'에 담아야 한다.

📝 4. 구현 코드

tree를 사용한 연계퀘스트에 중점을 맞췄기 때문에 다른 코드는 생략하였다.

- Quest 클래스

public abstract class Quest
{
    // 필드 
    protected string questName;   // 퀘스트이름
    protected string questStory;  // 퀘스트 스토리 (ex) 미니언들이 너무 많아졋다고 생각~
    protected int rewardGold;               // 리워드 
    protected string questPerform;          // 수행내역 
    protected QuestState questState;        // 수행 스탯 

    // 컨테이너
    protected Dictionary<Item, int> rewardItemByCount;        // 보상 아이템별 count
    protected Func<bool> completeQuest;                       // 성공여부 Func 

    // 트리구조
    protected Quest parentQuest;
    protected List<Quest> childQuest;

    // 프로퍼티
    public Quest ParentQuest { get => parentQuest; set { parentQuest = value; } }
    public List<Quest> ChildQuest => childQuest;

    public Quest(string name, string tooltip, string questPerfom , int rewardGold)
    {
        this.questName = name;
        this.questStory = tooltip;
        this.questPerform = questPerfom;
        this.questState = QuestState.beforeReceive;     // 생성할 때, 받기전으로 설정 
        this.rewardGold = rewardGold;

        if (rewardItemByCount == null)
            rewardItemByCount = new Dictionary<Item, int>();
        if(childQuest == null)
            childQuest = new List<Quest>(); 
    }

    public void AddToItem(Item item, int count)
    {
        // 코드생략
    }

    public void CheckState() 
    {
        // 받기전이면 return 
        if (questState == QuestState.beforeReceive)
            return;

        // 조건을 만족하면
        if (completeQuest.Invoke()) 
        {
            // 완료 state로 변환 
            this.questState = QuestState.complete;
        }
    }

    // child 자식 대입
    public void AddChild(Quest child) 
    {
        child.parentQuest = this;
        childQuest.Add(child);
    }
        // 하위에서 작성해야할 퀘스트 성공 조건
    public abstract bool TodoMission();

    // 퀘스트 진행내역
    public abstract string QuestProgress();
}

Quest 클래스

          ■ 자세한 설명은 상단의 클래스다이어그램 참고

           AddChild() 메서드

                        Quest클래스를 매개변수로 가진다

                        매개변수로 들어온 자식 퀘스트의 부모퀘스트를 this, 즉 내 클래스로 지정한다

                        현재 퀘스트의 자식클래스의 리스트에 매개변수로 들어온 자식 클래스를 add 한다

            CheckState() 메서드

                       Func<bool> completeQuest 를 Invoke()    

                                 ■ true를 return → complete로 상태 변환  

                                  flase를 return 상태 변환 x 

 

- MonsterKillQuest 클래스

public class MonsterKillQuest : Quest
{
    // 처치해야할 몬스터 
    // Monster 타입이면 더 좋을듯
    private List<string> monsterNameList;
    private List<int> killCountList;
    
    // 프로퍼티
    public List<string> MonsterNameList => monsterNameList;
    public List<int> KillCountList => killCountList;  

    public MonsterKillQuest(string name, string tooltip,string perForm , int rewardGold) : 
        base(name, tooltip , perForm , rewardGold)
    {
        // Func에 연결
        completeQuest += TodoMission;
    }

    public void AddtoKillMonsterList(string name , int cnt ) 
    {
        // 코드생략
    }

    public override bool TodoMission()
    {
        bool flag = true;

        // 지금까지 처치한 몬스터 수 
        for (int i = 0; i < monsterNameList.Count; i++) 
        {
            string nowMonster = monsterNameList[i];
            int countToKill = killCountList[i];

            // 잡은횟수
            int killCnt = DungeonManager.Instance.monsterCatches[nowMonster];

            // 처치한 몬스터가 kill count보다 낮으면 -> 실패
            if (killCnt < countToKill)
            { 
                flag = false;
                break;
            }
        }

        // for문안의 조건문에 걸리지않으면 -> killcount대로 다 잡은것 
        return flag;
    }

    public override string QuestProgress()
    {
        // 코드생략
    }

}

MonsterKillQuest 클래스

           자세한 설명은 상단의 클래스다이어그램 참고

            TodoMission() 메서드 오버라이딩 

                       퀘스트 완료 조건 구현

                      □ '현재 잡은 몬스터 수'가 '잡아야 하는 몬스터 수'보다 작으면 false를 return, 완료하면 true를 return

            생성자 

                      Func<bool> completeQuest 에 구현한 TodoMission() 메서드를 추가

 

- QuestManger 클래스

- 필드

private Quest currQuest;                // 현재 퀘스트 저장 

private List<Quest> performableQuests;  // 수행가능 퀘스트

currQuest

           현재 퀘스트 저장 

List<Quest> performableQuests

           수행가능한 퀘스트 컨테이너

           화면에 출력 할 때 해당리스트에 접근해서 퀘스트 이름을 출력

 

- Enter() 메서드

public void Enter()
{
    Console.Clear();

    // 수행가능한 퀘스트 이름만 빼서 배열로 저장 (LINQ)
    // 코드 생략
   
    // 목록(리스트) 출력
    // 코드 생략

    // player input 
    // 코드 생략

    // 1. 현재 퀘스트 
    currQuest = performableQuests[input - 1];
    // 2. 완료여부에 따라 state변화 
    currQuest.CheckState();

    // 퀘스트 print
    // 코드 생략

    // 성공여부에 따라 print 다름 
    PrintRewardBystate();
}

1. 플레이어에 입력에 맞게 Quest 타입의 currQuest를 저장한다.

2. currQuest의 상태변환 메서드를 실행한다 

           자세한 설명은 Quest 클래스의 CheskState() 메서드 참고

 

- PrintRewardBystate() 메서드

private void PrintRewardBystate()
{
    switch (currQuest.QuestState)
    {
        case QuestState.beforeReceive:
            // 코드생략
            break;
        case QuestState.afterReceive:
            // 코드생략
            break;
        case QuestState.complete:
            RemoveListAndAddToChild();
            break;
    }
}

1. 현재 퀘스트의 상태에 따라 다른 메서드를 실행한다.

2. 퀘스트가 complete 상태이면 (완료했으면) RemoveListAndAddToChild() 메서드를 실행한다. 

 

- RemoveAndAddToChild() 메서드

private void RemoveListAndAddToChild()
{
    // 현재 퀘스트의 state를 done으로
    currQuest.ChangeState(QuestState.done);

    // currQuest와 같은 quest 반환
    var temp = performableQuests.Find(quest => quest.Equals(currQuest));

    // 수행가능 리스트에서 삭제
    if (temp != null)
    {
        performableQuests.Remove(temp);
    }

    // 현재 퀘스트의 child리스트에 접근해서 가능한 퀘스트리스트에 넣어야 함 
    for (int i = 0; i < currQuest.ChildQuest.Count; i++)
    {
        performableQuests.Add(currQuest.ChildQuest[i]);
    }
}

1. 현재 퀘스트의 상태를 QuestState.done으로 변경한다.

2. 수행가능한 퀘스트를 담아놓는 리스트에서 현재 퀘스트를 remove 한다. 

3. 현재 퀘스트의 List<Quest>에 접근해서 수행가능한 퀘스트 리스트(perforambleQuest)에 추가한다.


📝 동작 영상

 

 


📝 마무리

: 현재는 수락불가능한 퀘스트가 출력되고 있지는 않다. 

유니티 개인 프로젝트를 할 때 수락 불가능한 퀘스트도 출력해서 퀘스트의 부모 퀘스트 추적 기능까지 추가해 보겠다.

📝 부모퀘스트 추적

(추가예정입니다)

 


TextRpg 팀 프로젝트 깃허브 주소 : 

https://github.com/kimYouChae/Window11_TextRPG

 

GitHub - kimYouChae/Window11_TextRPG

Contribute to kimYouChae/Window11_TextRPG development by creating an account on GitHub.

github.com

 

 

 

🔖 메모리 구조에 대해서 잘 모르시는 분들은 이전 글을 참고해 주세요!

  2025.02.12 - [c#] - [c#]힙메모리와 스택 메모리

 

[c#]힙메모리와 스택 메모리

메모리의 공간 1. 코드 (code) 영역2. 데이터 (data) 영역3. 스택 (stack) 영역4. 힙 (heap) 영역  □  코드(code) 영역            ■ 실행할 프로그램의 코드가 저장되는 영역            ■ 텍스트

youcheachae.tistory.com

 

📝 스택 프레임

  스택 프레임이란 무엇인가 ? (stack frame)

              스택 영역에 차례대로 저장되는 함수의 호출정보

              함수가 호출될 때 스택에 저장되는 내용 

                        함수의 매개변수

                         호출이 끝난 뒤 돌아갈 반환 주소 값

                         함수에서 선언된 지역변수

              스택에 올라간 함수가 모두 끝난뒤에,

           스택 프레임에 저장된 정보를 바탕으로 함수가 호출되기 이전 상태로 돌아갈 수 있다 

 

📝 스택 프레임의 동작 방식  

public static void Fun1() 
{
    Fun2();     // Fun2() 호출
}

public static void Fun2() 
{

}

static void Main(string[] args)
{

    Fun1();	// Fun1() 호출
}

 

  메서드 호출 시 동작

1. 프로그램이 실행되면, 가장 먼저 main() 함수가 호출되어 main()의 스택 프레임이 스택에 저장됨

2, fun1() 함수를 실행하면, 해당함수의 매개변수, 반환 주소값, 지역 변수 드으이 스택 프레임이 스택에 저장됨

3. fun2() 함수를 실행하면, 해당함수의 매개변수, 반환 주소값, 지역 변수 드으이 스택 프레임이 스택에 저장됨

 

  메서드 완료 시 동작

1. fun2()함수의 모든 작업이 완료되면, fun2()의 스택프레임만이 스택 메모리에서 제거됨

2. fun1()함수의 모든 작업이 완료되면, fun2()의 스택프레임만이 스택 메모리에서 제거됨

3. main()함수의 모든 작업이 완료되면 , main()의 스택프레임만이 스택 메모리에서 제거됨

4. 프로그램이 종료

 


스택프레임

https://www.tcpschool.com/c/c_memory_stackframe

 

메모리의 공간

 

1. 코드 (code) 영역

2. 데이터 (data) 영역

3. 스택 (stack) 영역

4. 힙 (heap) 영역

 

 

  코드(code) 영역 

           실행할 프로그램의 코드가 저장되는 영역 

           텍스트 영역 

 

  데이터(data) 영역 

           프로그램의 전역 변수와 정적 변수가 저장되는 영역 

           프로그램 시작과 함께 할당되며, 프로그램이 종료되면 소멸 

 

  스택 (stack) 영역

           함수의 호출과 관계되는 지역변수와 매개변수가 저장되는 영역

           함수의 호출과 함께 할당, 함수의 호출이 완료되면 소멸

           후입선출(LIFO, Last-In First-Out)

                      □ 푸시 (Push) 동작 : 데이터를 저장

                       팝 (Pop) 동작 : 데이터 인출

                       가장 늦게 저장된 데이터가 가장 먼저 인출됨

            높은 주소에서 낮은 주소의 방향으로 할당

 

  (heap) 영역

            사용자가 직접 관리할 수 있는 , 해야만 하는 영역

            사용자에 의해 메모리 공간이 동적으로 할당되고 해제됨.

            낮은 주소에서 높은 주소의 방향으로 할당

 


 

🔖 static선언을 하면 어느 메모리로 할당될까?

🔖 스택오버플로우라는 단어는?

🔖 스택메모리와 힙 메모리의 차이

🔖 스택프레임이란?

🔖 메서드를 호출할 때 어떤 순서로 메모리에 할당될까?


 

메모리구조

https://www.tcpschool.com/c/c_memory_structure

 

코딩교육 티씨피스쿨

4차산업혁명, 코딩교육, 소프트웨어교육, 코딩기초, SW코딩, 기초코딩부터 자바 파이썬 등

tcpschool.com

 

1. LINQ
    □ 개념
    □ 장/단점
2. 쿼리구문
3. 메서드 구문
4. 혼합된 쿼리 / 메서드 구문 
5. 직접 사용 예시

0. LINQ에 관한 다른 글

 

📝 1. LINQ 

□ Language-Integrated Query의 약자

             직역하면 "언어 통합 쿼리"

□ 관계형 데이터나 XML 데이터뿐만 아니라 모든 c# 데이터 컬렉션에 적용되는 일관된 쿼리  

 

🔖  장점

1. 데이터베이스, XML , 모든 c# 데이터 컬렉션에 동일한 쿼리 사용가능

2. 쿼리에서 사용되는 데이터 타입이 컴파일 타임에 체크됨       

             런타임에서 발생할 수 있는 오류를 줄임 

3. 데이터 처리 관련 로직을 간결하게 만들어줌

4. 데이터 필터링, 정렬, 집계, 조인 등의 작업을 간편하게 처리가능

 

🔖  단점

1. 복잡한 쿼리에서는 가독성이 떨어질 수도 있음

2. 대량의 데이터 처리 시 성능저하가 발생할 수도 있음

 

📝 2. Query Syntax ( 쿼리 구문 ) 형식

 □ from : 어느 데이터에서 가져올 것인지    

            num : 쿼리문 안에서 사용할 데이터 이름

□ where : 쿼리문에서 필터링할 조건

□ select : 조건에 만족하는 반환할 데이터 

 

예시

int[] numbers = [ 0, 1, 2, 3, 4, 5, 6 ];

// numQuery 는 IEnumerable<int> 타입
var numQuery = from num in numbers
               where (num % 2) == 0
               select num;

□ from :  int형 배열 numbers에서 가져옴

□ where : 배열 안의 값이 2로 나누어 떨어지면 (즉 짝수이면)

□ select : 값을 리턴한다

출력 [0, 2, 4, 6]

 

📝 3. Method Syntax ( 메서드 구문 ) 형식

: Sum, Max, Min, Average 등과 같은 함수를 사용한 쿼리 구문

 

예시

List<int> numbers1 = [ 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 ];
List<int> numbers2 = [ 15, 14, 11, 13, 19, 18, 16, 17, 12, 10 ];

// 메서드 구문 1
double average = numbers1.Average();

// 메서드 구문 2
IEnumerable<int> concatenationQuery = numbers1.Concat(numbers2);

□ 메서드 구문 1

            number1라는 int 타입 리스트에서의 평균값을 구한 후 return

□ 메서드 구문 2

            numbers1 리스트와 numbers2를 합침. 

            두 배열이 합쳐진 새로운 배열을 return 

 

📝 4. 혼합된 쿼리 / 메서드 구문 

var numCount1 = (
    from num in numbers1
    where num is > 3 and < 7
    select num
).Count();

□ 쿼리구문

           ■ numbers1 리스트에서 3 초과 7 미만의 값을 고른 후 select

□ 메서드 구문 

           ■반환된 IEnumerable<int 타입의 Count를 return

 

🔖  공식 c# 홈페이지에서 추천하는 방법

IEnumerable<int> numbersQuery =
    from num in numbers1
    where num is > 3 and < 7
    select num;

var numCount2 = numbersQuery.Count();

  쿼리 구문을 사용한 후

 새 변수를 할당해서 IEnumerable<intIEnumerable <int>의 Count를 return

 

📝 5. 직접 사용 예시

내일배움캠프 c# 프로그래밍-TextRpg-팀 프로젝트 중에 사용한 코드입니다.


🔖
 람다식을 사용한 메서드 구문 1

int count = InventoryManager.instance.mountableItems
    .Count(item => item.Type == questItemType && item.Equip);

  인벤토리 매니저의 List<MountableItems> 리스트에 접근

  Linq의 메서드 구문인 Count() 사용

            조건

                       : 아이템이 type이 questItemType과 같고

                       : 착용 여부를 나타내는 Equip변수가 true인 데이터

  결과

           ■ List<Mountable> mountablesItems 리스트에 들어있는 아이템 중에

            조건에 만족하는 아이템 개수를 알 수 있음.

 

🔖 람다식을 사용한 메서드 구문 2

public class Quest
{
    private string questName;   // 퀘스트이름
    
    // 프로퍼티
    public string QuestName => questName;
    
    public Quest(string name)
    {
        this.questName = name;
    }
}
private List<Quest> quests;     // 퀘스트 컨테이너 
private string[] questName;     // 퀘스트 이름 목록

quests.Add(new Quest(name: "마을을 위협하는 미니언 처치");
quests.Add(new Quest(name: "장비를 장착해보자");
quests.Add(new Quest(name: "더욱 더 강해지기!");

// quest에서 이름만 빼서 배열로 저장 (LINQ)
questOption = quests.Select(q => q.QuestName).ToArray();

Quest 클래스 인스턴스화 한 뒤 List<Quest>에 추가 

List<Quest>에서 QuestName을 select 해서 배열로 변환

  결과

            questName[0] : 마을을 위협하는 미니언처치

            questName[1] : 장비를 장착해 보자

            questName[2] : 더욱더 강해지기 

            배열에 Quest 클래스의 제목이 차례대로 들어가게 됨

 


📝 LINQ에 관한 다른 글

추가예정  


LINQ란

https://learn.microsoft.com/ko-kr/dotnet/csharp/linq/

 

C#의 LINQ(Language-Integrated Query) - C#

C#의 LINQ(Language-Integrated Query) 소개

learn.microsoft.com

 

컴파일 타임 때 타입 체크 

https://learn.microsoft.com/ko-kr/dotnet/csharp/linq/get-started/type-relationships-in-linq-query-operations

 

LINQ 쿼리 작업의 형식 관계 - C#

LINQ 쿼리의 변수 형식이 서로 관련되는 방식에 대해 알아봅니다. LINQ 쿼리 작업은 데이터 원본, 쿼리 및 실행에서 강력하게 형식화됩니다.

learn.microsoft.com

 

쿼리 구문과 메서드 구문 

https://learn.microsoft.com/ko-kr/dotnet/csharp/linq/get-started/write-linq-queries

 

LINQ 쿼리 작성 - C#

C#에서 LINQ 쿼리를 작성하는 방법을 알아봅니다.

learn.microsoft.com

 

1. delegate
    □ 개념
    □ 예시

📝 delegate (대리자)

1. 개념

메서드에 대한 참조를 나타내는 형식

C++의 함수 포인터와 비슷하게 동작함!

           ■함수포인터?

           : 함수의 시작 주소를 가리키는 포인터

           : 함수를 간접적으로 호출할 수 있게 해주는 변수

 

참조

하나 이상의 메서드를 참조할 수 있다.

참조할 메서드는 같은 반환값과 같은 매개변수를 가져야 한다.

□ += 연산자로 메서드 추가, -= 연산자로 메서드 제거가 가능하다.

 

호출

 메서드를 직접 호출하지 않고 메서드를 참조하고 있는 델리게이트를 통해 호출한다.

 델리게이트를 실행하면 참조하고 있는 모든 메서드가 실행된다.

 

📝 예시 

// 델리게이트
public delegate void Attack(float damage);
public class AttackHandler
{
    public Attack OnTtack;

    public void AttackExcute(float damage)
    {
        OnTtack?.Invoke(damage);
    }

    public void AddToAttack(Attack attck) 
    {
        OnTtack += attck;
    }
}

public class Player
{
    public float hp;
    public float damage;

    public Player(float hp,float d) 
    {
        this.hp = hp;
        this.damage = d;
    }

    public void GetAttack(float damage) 
    {
        hp -= damage;
        Console.WriteLine($"{damage} 데미지를 얻었습니다.");
    }
}

public class SystemAlarm
{
    public void UpdateUi(float damage) 
    {
        Console.WriteLine($"player가 {damage} 를 입었습니다.");
    }
}
    static void Main(string[] args)
    {
        // 
        AttackHandler AKHandler = new AttackHandler();

        // 인스턴스 생성
        Player player = new Player(10f, 3f);
        SystemAlarm alarm = new SystemAlarm();

        AKHandler.AddToAttack(player.GetAttack);
        AKHandler.AddToAttack(alarm.UpdateUi);

        // 델리게이트 실행
        AKHandler.AttackExcute( 7f );
    }

Attack 델리게이트

           float 매개변수를 받는 메서드들을 참조하는 형식

            float 매개변수를 받는 Player와 SystemAlarm 메서드 등록

  AttackExcute 호출 시 두 메서드가 순차적으로 실행

 

출력


함수포인터

https://www.tcpschool.com/cpp/cpp_function_pointer

 

코딩교육 티씨피스쿨

4차산업혁명, 코딩교육, 소프트웨어교육, 코딩기초, SW코딩, 기초코딩부터 자바 파이썬 등

tcpschool.com

델리게이트

https://learn.microsoft.com/ko-kr/dotnet/csharp/programming-guide/delegates/using-delegates

 

대리자 사용 - C#

대리자를 사용하는 방법을 알아봅니다. 대리자는 메서드를 안전하게 캡슐화하는 개체 지향적이고 형식이 안전하며 보안이 유지되는 형식입니다.

learn.microsoft.com

 

+ Recent posts