[C#] IEnumerable, Linq 사용 예제

유니티에서 Foreach 를 실행시 Garbage를 생성시키는것을 피하기위해서 주로 사용 했었는데, 근래에는 Foreach를 사용해도 Garbage를 생성 하지 않는다 한다.

다음목표

  1. IEnumerable 를 어떤 케이스에서 쓰는게 효율적일지 생각해보자.
  2. Linq 는 어떤 상황일때 쓰는게 좋을지 생각해보자.
using UnityEngine;
using System.Collections;
using System.Collections.Generic; // IEnumerable 쓰려고.
using System.Linq;

public class Bong9_Enumerable : MonoBehaviour {

	// Use this for initialization
	void Start () {

        ///
        /// string 객체에는 IEnumerable 인터페이스가 구현 되어있다.
        /// 

        string[] strList = { "bong9", "bong8", "bong7", "b09", "b08", "b07" };

        // 구현.
        Debug.Log("------IEnumerable() Start ------");
        IEnumerable(strList);
        Debug.Log("------IEnumerable() End ------");

        // Linq 사용.
        Debug.Log("------IEnumerable_Linq() Start ------");
        IEnumerable_Linq(strList);
        Debug.Log("------IEnumerable_Linq() End ------");
	}

    void IEnumerable_Linq(string[] strList)
    {
        // 조건 : 글자수가 3개 이하.
        IEnumerable enumerable = from str in strList where str.Length <= 3 select str;

        IEnumerator e = enumerable.GetEnumerator();

        while (e.MoveNext())
        {
            Debug.Log(e.Current);
        }
    }

    void IEnumerable(string[] strList)
    {
        IEnumerator e = strList.GetEnumerator();

        while (e.MoveNext())
        {
            Debug.Log(e.Current);
        }
        // Debug.Log(e.Current); // 이미 열거가 완료되어 에러.

        e.Reset(); // 열거 초기화.
        e.MoveNext();
        Debug.Log(e.Current + "열거 초기화 후 다시 출력.");
    }

	// Update is called once per frame
	void Update () {

	}
}

[C#] Action 과 Delegate, 람다식, Action, Func, Event

  •  Delegate ( 위임자 )
    • 일종의 함수 포인터
    • 내부적으로 Count 만큼 List를 순회하는 연결리스트 구조를 생성
  • Action의 정의
    • Action은 반환값과 인자값이 없는 함수 포인터 ( 델리게이트 )
    • 반환 형식이 없음
    • 어떤 결과를 반환하는것을 목적으로 하지 않으며, 일련의 작업 수행을 목적
/* Action 델리게이트 예제 */
    void Start()
    {
        Action act1 = () => Debug.Log("Action()");
        act1();

        int re = 0;
        Action act2 = (x) => re = x * x; // 람다식 밖에서 선언한 re에 결과값 저장.
        act2(3);

        Debug.Log("re : " + re);

        Action act3 = (x, y) =>
        {
            double pi = x / y;
            Debug.Log("Action(" + x + ", " + y + ") : " + pi);
        };

        act3(22.0, 7.0);
    }
using System.Runtime.CompilerServices;

namespace System
{
      public delegate void Action();
}
  • Func ( 델리게이트 )
    • 일종의 함수 포인터
    • 내부적으로 Count 만큼 List를 순회하는 연결리스트 구조를 생성
    • 익명메소드, 무명함수를 만들기위해서는 항상 델리게이트를 선언 해야 하지만 Func를 사용하면 끝

using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;

public class Tester : MonoBehaviour {

    public Action myAction;

    public void SetAction(Action func)
    {
        myAction += func;
    }

    public static void Func1() { Debug.Log("Func1 실행"); }
    public static void Func2() { Debug.Log("Func2 실행"); }

    // SetAction으로 함수를 등록한후,
    void Start()
    {
        Tester ActionTest = new Tester();
        ActionTest.SetAction(Func1);
        ActionTest.SetAction(Func2);
        ActionTest.myAction();
    }
}
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Text;

public class Tester : MonoBehaviour {

    public delegate int Print (string msg );

    public static int SpeakA(string msg)
    {
        Debug.Log("A : " + msg);
        return 0;
    }
    public static int SpeakB(string msg)
    {
        Debug.Log("B : " + msg);
        return 1;
    }

    void Start()
    {
        Print A = new Print(SpeakA);
        //Print B = new Print(SpeakB);

        A("Hello");
        //B("Hello");
    }
}
void Start()
    {
        Func func1 = () => 10;             // 바로 10을 리턴.
        Func func2 = (x) => x * 2;         // 넣어준 인수의 2를 곱한값 리턴.
        Action act = (x) =>                // 리턴없는 Action 함수.
        {
            Debug.Log(x);                  // Debug.Log로 직접 출력.
        };

        Debug.Log(func1());
        Debug.Log(func2(3));
        act(10);
    }
/* 람다식 (Lambda expression) 선언 예제 */
    delegate int Calculate(int a, int b);
    delegate void DoSomething();

    void Start()
    {
        Calculate cal = (a, b) => a + b;
        Debug.Log(cal(1, 3));

        DoSomething Doit = () =>
        {
            Debug.Log("두잇.");
        };

        Doit();
    }

/* 익명메소드를 이용한 델리게이트 사용 */
    delegate string Fire(string location);

    void Start()
    {
        Fire fire;
        fire = delegate(string location)
        {
            return location;
        };

        Debug.Log(fire("here"));
    }

/* 델리게이트 체인 */
    delegate void MyFire(string location);

    public static void call119(string location)
    {
        Debug.Log("불난곳: " + location);
    }

    public static void shout(string location)
    {
        Debug.Log("소리친곳: " + location);
    }

    public static void escape(string location)
    {
        Debug.Log("도망가는곳: " + location);
    }

    void Start()
    {
        MyFire fire = new MyFire(call119);

        fire += new MyFire(call119);    // 첫번째 델리게이트 체인 등록.
        fire += new MyFire(shout);      // 두번째 델리게이트 체인 등록.
        fire += new MyFire(escape);     // 세번째 델리게이트 체인 등록.

        fire("우리집에서");              // 함수포인터(델리게이트)에 등록이된 함수들을 실행.
    }
/* 문형식의 람다식 */

    string[] args = { "hi", "hello", "good" };

    delegate string Co(string[] args);

    void Start()
    {
        Co c = (arr) =>
        {
            string re = "";
            foreach (string s in arr)
            {
                re += s;
                Debug.Log("Add string is " + s);
            }
            return re;
        };
        Debug.Log(c(args));
    }
  • Event
    • 이벤트는 델리게이트에 event 한정자를 수식해서 선언한것에 불과하다
    • 이벤트와 델리게이트의 차이점은 이벤트는 public으로 선언되어 있어도 외부에서 사용할 수 가 없다.
    • 델리게이트는 public, internal로 수식되어 있으면 클래스 외부에서도 얼마든지 호출이 가능하다
    /* 이벤트, 델리게이트 예제 01 */
    delegate void EventCaller(string m);

    class CallEvent
    {
        public event EventCaller ec;
    }

    void Start()
    {
        CallEvent callEvent = new CallEvent();
        //callEvent.ec("test"); // 에러, 이벤트는 객체 외부에서 직접 호출 할 수 없다.
    }
/* 이벤트, 델리게이트 예제 02 */
    delegate void EventHandler(string m);

    class Notification
    {
        public event EventHandler eventHandler;

        public void EvenOdd(int number)
        {
            int temp = number % 10;

            if (temp != 0 && temp % 3 == 0)
            {
                eventHandler(string.Format("{0} : Even", number));
            }
        }
    }

    public static void MEventHandler(string m)
    {
        Debug.Log(m);
    }

    public static void SEventHandler(string m)
    {
        Debug.Log(m + "SEvebtHandler");
    }

    public static void XEventHandler(string m)
    {
        Debug.Log(m + "XEvebtHandler");
    }

    void Start()
    {
        Notification noti = new Notification();
        noti.eventHandler += new EventHandler(MEventHandler);
        noti.eventHandler += new EventHandler(SEventHandler);
        noti.eventHandler += new EventHandler(XEventHandler);

        for (int i = 1; i < 30; i++)
        {
            noti.EvenOdd(i);
        }
    }

[C#] Delegate And Func

Delegate and Func

  • .NET Framework에는 총 17 가지의 Func 델리게이트가 준비 되어있다.
  • 매개 변수가 없는 메소드부터 매개 변수가 16개인 메소드까지 총 17개의 메소드를 참조 가능.
  • 무명 메소드 뿐만 아니라 일반 메소드도 참조 가능.
  • Func 델리게이트로 메소드를 참조하면 전처럼 델리게이트 타입을 선언하는 과정이 불필요 해지므로 아주 간결하게 코드를 작성할 수 있다.

/**
 * @file    ExamFuncDelegate01.cs
 * @brief   델리게이트와 Func.
 * @details 델리게이트와 Func 사용 예제.
 * @version  0.0.1
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace StudyCSharp_Bong9
{
    namespace ExamFuncDelegate01
    {
        /**
         * @brief    델리게이트와 Func 예제 클래스.
         * @details  Func와 Action은 미리 선언된 델리게이트 변수로써 별도의 선언없이 사용이 가능. Func는 반환값이 있는 메소드를 참조하는 델리게이트 변수, Action은 반환값이 없는 메소드를 참조하는 델리게이트 변수.
         * @author   김봉재.
         * @date     2014-08-07
         */
        class ExamFuncDelegate
        {
            static float temp (int a, int b, int c)
            {
                return (a + b + c) * 0.1f;
            }

            static void Main(string[] args)
            {
                Func float             func0 = () =&gt; 0.1f;
                Func int, float        func1 = (a) =&gt; a * 0.1f;
                Func int, int, float&gt;   func2 = (a, b) =&gt; (a+b) * 0.1f;

                Func<span id="mce_SELREST_start" style="overflow:hidden;line-height:0;">&#65279;</span>&lt;int, int, int, float func3;
                func3 = new Func int, int, int, float(temp);

                Console.WriteLine(&quot;func0 반환값: {0}&quot;, func0());
                Console.WriteLine(&quot;func1 반환값: {0}&quot;, func1(10));
                Console.WriteLine(&quot;func2 반환값: {0}&quot;, func2(10,10));
                Console.WriteLine(&quot;func3 반환값: {0}&quot;, func3(10,10,10));
            }
        }
    }
}

[C#] Event, Delegate 01, 02 – 델리게이트와 이벤트

• Event, Delegate

  • 델리게이트를 선언하고, 등록하고, 사용하는법.

• ExamEventAndDelegate01.cs

/**
 * @file    ExamEventAndDelegate01.cs
 * @brief   델리게이트와 이벤트.
 * @details 델리게이트와 이벤트의 사용 예제.
 * @version  0.0.1
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace StudyCSharp_Bong9
{
    namespace ExamEventAndDelegate01
    {
        /**
         * @brief    델리게이트와 이벤트 예제 클래스.
         * @author   김봉재.
         * @date     2014-08-07
         */
        class ExamEventAndDelegate
        {
            ///@brief 이벤트 핸들러용 델리게이트 선언.
            public delegate void MyEventHandler(string message);

            class Publisher
            {
                ///@brief event 한정자로 수식한 델리게이트의 인스턴스를 선언.
                public event MyEventHandler Active;

                public void DoActive(int number)
                {
                    if (number % 10 == 0)
                    {
                        Active("Active" + number);
                    }
                    else
                    {
                        Console.WriteLine(number);
                    }
                }
            }

            class Subscriber
            {
                static public void MyHandler(string message)
                {
                    Console.WriteLine(message);
                }

                static void Main(string[] args)
                {
                    ///@brief publisher 객체 생성.
                    Publisher publisher = new Publisher();

                    ///@brief publisher 객체에 접근, Active 델리게이트의 인스턴스에 이벤트핸들러 등록(MyHandler).
                    publisher.Active += new MyEventHandler(MyHandler);

                    for (int i = 1; i <span id="mce_SELREST_start" style="overflow:hidden;line-height:0;">&#65279;</span>&lt; 50; i++)
                    {
                        ///@brief 조건에 따라, Active event에 등록되어있는 함수를 델리게이트가 실행.
                        publisher.DoActive(i);
                    }
                }
            }
        }
    }
}

• ExamEventAndDelegate02.cs

/**
 * @file    ExamEventAndDelegate02.cs
 * @brief   델리게이트와 이벤트.
 * @details 델리게이트와 이벤트의 사용 예제.
 * @version  0.0.1
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace StudyCSharp_Bong9
{
    namespace ExamEventAndDelegate02
    {
        /**
         * @brief    델리게이트와 이벤트 예제 클래스.
         * @author   김봉재.
         * @date     2014-08-07
         */
        class ExamEventAndDelegate
        {
            ///@brief 1.델리게이트 선언.
            delegate void MyDelegate(int a);

            class EventManager
            {
                ///@brief 2.델리게이트형식의 이벤트 선언.
                public event MyDelegate eventCall;

                public void NumberCheck(int num)
                {
                    if (num % 2 == 0)
                    {
                        eventCall(num);
                    }
                }
            }

            class MainApp
            {
                ///@brief 3.델리게이트에서 등록되어 사용될 이벤트 함수 작성.
                static void EventNumber(int num)
                {
                    Console.WriteLine("{0}는 짝수", num);
                }

                static void Main(string[] args)
                {
                    ///@brief 4.이벤트메니져 클래스 초기화.
                    EventManager eventManager = new EventManager();

                    ///@brief 5.델리게이트에 사용할 함수를 등록.
                    eventManager.eventCall += new MyDelegate(EventNumber);

                    for (int i = 1; i < 10; i++)
                    {
                        ///@brief 6.이벤트 메니져의 델리게이트에 함수 호출.
                        eventManager.NumberCheck(i);
                    }
                }
            }
        }
    }
}

[C#] Delegate 델리게이트 무명 메서드.

 • 델리게이트 무명 메서드 예제.

  • 익명메서드, 익명대리자,
  • 메서드 인데 이름이 없음. 인스턴스 식으로 그때그때 간단히 사용할 때 씀.

• ExamDelegate03.cs

/**
 * @file    ExamDelegate03.cs
 * @brief   델리게이트 무명 메서드.
 * @details 델리게이트 무명 메서드 사용 예제.
 * @version  0.0.1
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace StudyCSharp_Bong9
{
    namespace ExamDelegate03
    {
        /**
         * @brief    델리게이트 무명메서드 예제 클래스.
         * @author   김봉재.
         * @date     2014-08-07
         */
        class ExamDelegate
        {
            ///@brief 델리게이트 선언.
            delegate int DelegateMethod(int a, int b);

            static void Main(params string[] args)
            {
                DelegateMethod method = null;
                ConsoleKeyInfo c;
                bool menu = true;
                int ia = 0, ib = 0;

                while (true)
                {
                    while (menu)
                    {
                        Console.WriteLine("A. 덧셈");
                        Console.WriteLine("B. 뺄셈");
                        Console.WriteLine("C. 곱셈");
                        Console.WriteLine("Q. 끝");
                        Console.Write("Press a Key : ");

                        c = Console.ReadKey(false);

                        Console.Write('\n');

                        switch (c.KeyChar)
                        {
                            case 'A' :
                            case 'a' :
                                method = delegate(int a, int b) { return a + b; };
                                menu = false;
                                break;
                            case 'B':
                            case 'b':
                                method = delegate(int a, int b) { return a - b; };
                                menu = false;
                                break;
                            case 'C':
                            case 'c':
                                method = delegate(int a, int b) { return a * b; };
                                menu = false;
                                break;
                            case 'Q':
                            case 'q':
                                return;
                            default :
                                Console.WriteLine(" ?? Press A,B,C and Q ");
                                break;
                        }
                    }

                    Console.Write("첫번째 수를 입력하세요 : ");
                    ia = int.Parse(Console.ReadLine());
                    Console.Write('\n');

                    Console.Write("두번째 수를 입력하세요 : ");
                    ib = int.Parse(Console.ReadLine());
                    Console.Write('\n');

                    Console.WriteLine("(a = {0})와 (b = {1}) 의 결과는 {2} 입니다.", ia, ib, method(ia, ib));
                    menu = true;

                    Console.Write('\n');
                    Console.Write('\n');
                }
            }
        }
    }
}

[C#] Delegate 02 – 델리게이트 연쇄(Chain) 등록, 해제

• .NET의 Delegate 연쇄호출(Chain) 등록 및 해제

  • 델리게이트에 함수를 등록 하게 되면, 메소드를 사용 할때마다, 등록 되어있는 함수 전부에 메세지가 날라감, 필요에 따라서 델리게이트에 등록된 메소드를 해제 하면, 해제한 메소드 를 제외한 등록되어 있는 메소드들에게 메세지가 날라감.
  • 반환형은 반드시 void 로 되어있어야함.

• ExamDelegate02.cs

/**
 * @file    ExamDelegate02.cs
 * @brief   델리게이트 연쇄호출(Chain) 예제.
 * @details 델리게이트 연쇄호출(Chain) 사용법.
 * @version  0.0.1
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace StudyCSharp_Bong9
{
    namespace ExamDelegate02
    {
        /**
         * @brief    델리게이트 연쇄호출(Chain) 클래스.
         * @author   김봉재.
         * @date     2014-08-07
         */
        class ExamDelegate
        {
            ///@brief Delegate 연쇄호출 등록을 위해서는 void 형식이어야함.
            delegate void DelegateMethod(int a);

            static void Method1(int a)
            {
                Console.WriteLine("Method1 이 호출. a : {0}", ++a);
            }
            static void Method2(int a)
            {
                Console.WriteLine("Method2 이 호출. a : {0}", ++a);
            }
            static void Method3(int a)
            {
                Console.WriteLine("Method3 이 호출. a : {0}", ++a);
            }

            static void Main(params string[] args)
            {
                DelegateMethod method = null;

                ///@brief Delegate 연쇄호출 등록.
                method += Method1;
                method += Method2;
                method += Method3;

                ///@brief Delegate 연쇄호출 삭제.
                method -= Method1;

                ///@brief Delegate 실행.
                method(0);
            }
        }
    }
}

[C#] Delegate 란

• Delegate 란.

  • C언어의 함수포인터를 그대로 차용한것.
  • 메서드의 위치를 간직하고 있고, 그 메서드를 대신 실행.
  • 해당 메서드를 직접 실행 할 수 없는경우.
  • 메서드가 외부 메서드에 있거나, 런타임 도중 동적으로 바뀌는 경우 등등.

• ExamDelegate01.cs

/**
 * @file    ExamDelegate01.cs
 * @brief   델리게이트 예제.
 * @details 델리게이트 사용법.
 * @version  0.0.1
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace StudyCSharp_Bong9
{
    namespace ExamDelegate01
    {

        /**
         * @brief    델리게이트 예제 Basic 클래스.
         * @author   김봉재.
         * @date     2014-08-07
         */
        class ExamDelegate
        {
            ///@brief int를 리턴해주고, int a, int b의 파라메터를 가지는 형식의 함수를 대신 호출 해주겠음. 이라는 의미의 델리게이트(대리자)선언.
            delegate int DelegateMethod(int a, int b);

            static int Average(int a, int b)
            {
                return (int)((a + b) / 2);
            }

            static void Main(string[] args)
            {
                ///@brief Average메서드를 미리 선언한 델리게이트 형식으로 간접 호출.
                DelegateMethod method = Average;
                int a;
                int b;

                Console.Write("값 a를 입력하세요 : ");
                a = int.Parse(Console.ReadLine());
                Console.Write("\n");

                Console.Write("값 b를 입력하세요 : ");
                b = int.Parse(Console.ReadLine());
                Console.Write("\n");

                ///@brief method(a,b)로 간접 호출.
                Console.WriteLine("( a = {0} ) 과 ( b = {1} )의 평균값은 {2}입니다.", a, b, method(a, b));
            }
        }
    }
}