1. 델리게이트 ( Delegate )


델리게이트는 메서드를 참조하여 대신 일을 하는 대리자라 할수 있습니다.

메서드를 참조한다는 것은 매개변수 대신 메서드를 매개변수로 사용할수 있다는 말입니다.

개별적으로 사용도 가능하고 복수의 메서드들을 사용할수도 있습니다.

사용시 주의해야 될점은 델리게이트와 참조하여 사용하는 메서드들의 구성형식(반환타입, 매개변수...)이 같아야

한다는 것입니다.


서식형식

[한정자] delegate [반환형식] [델리게이트 이름] ( [매개변수] );

소스코드

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Function()
        {
            Console.WriteLine("Function Call!");
        }
        static void Function2()
        {
            Console.WriteLine("Function2 Call!");
        }
        static void Function3()
        {
            Console.WriteLine("Function3 Call!");
        }
        //일을 대신할 델리게이트 참조자를 선언
        delegate void Work();

        static void Main(string[] args)
        {
            //델리게이트 인스턴스 생성(단일 목적으로 사용)
            Work w1 = new Work(Function);
            w1();

            //기존의 참조된 메서드는 해제되고 새로운 메서드를 참조
            w1 = new Work(Function2);
            w1();

            Console.WriteLine();

            // +, -, +=, -= 연산자로 복수의 참조자를 늘릴수도 줄일수도 있습니다.
            w1 = new Work(Function);
            w1 += new Work(Function2);
            w1 += new Work(Function3);
            w1();

            w1 -= Function2;
            Console.WriteLine();
            w1();
        }
    }
}

결과

Function Call!
Function2 Call!

Function Call!
Function2 Call!
Function3 Call!

Function Call!
Function3 Call!


위와 같이 델리게이트는 연산자를 이용하여 추가할수도 삭제할수도 있습니다.

일종의 메서드배열과 같이 델리게이트를 실행하면 참조되고있는 모든 메서드들을 실행하게 됩니다.

또한 델리게이트를 인스턴스화 하여 객체를 매개변수로 전달할수도 있습니다.




2. 이벤트 ( Event )


이벤트는 컴퓨터에서 특정한 일이 일어나게 되면 발생하는 메세지라 할수 있습니다.

일반적으로 많이 사용하는 windows 운영체제 역시 이 이벤트 기반으로 만들어져 있으며 기능들을 수행하게 됩니다.

특정 시간이되면 알려주는 이벤트라던지 사용자가 주변기기를 사용하여 마우스를 이동, 클릭, 드래그 등...

이벤트와 델리게이트의 차이점은 외부에서 직접적인 접근이 가능하냐 그렇지 못하냐의 차이가 있습니다.


서식 형식

[접근 한정자] event [델리게이트 명];

소스코드

using System;

namespace ConsoleApp1
{
    delegate void EventHandler(int number, string message);

    //방문자 이벤트를 발생할 클래스
    class Visitor
    {
        public event EventHandler EventCall;
        
        public void VisitorCheck(int VisitorNum)
        {
            //방문자가 3의 배수일때마다 이벤트 발생
            if (VisitorNum % 3 == 0)
                EventCall(VisitorNum, "방문자");
        }
    }

    //이벤트를 받아 처리할 
    class Celebration
    {
        public void VisitorEvent(int number, string message)
        {
            if (number == 9)
            {
                Console.WriteLine("---축하합니다---");
                Console.WriteLine("{0}번째 {1}로 이벤트에 당첨 되셨습니다.", number, message);
            }
            else
                Console.WriteLine("방문자 이벤트 발생!");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            //이벤트를 받을 객체 인스턴스
            Celebration Cele = new Celebration();

            //이벤트 발생 인스턴스
            Visitor Vis = new Visitor();
            //이벤트가 발생될때 호출될 메서드 추가
            Vis.EventCall += new EventHandler(Cele.VisitorEvent);

            for(int i = 0; i < 10; i++)
            {
                Vis.VisitorCheck(i);
            }
        }
    }
}

결과

방문자 이벤트 발생!
방문자 이벤트 발생!
방문자 이벤트 발생!
---축하합니다---
9번째 방문자로 인벤트에 당첨 되셨습니다.


이러한 이벤트 기반 프로그래밍은 프로그램의 상태변화에 따라 델리게이트를 호출하여 안정적인 데이터 관리와

공정하게 이벤트 상황을 전달할수 있습니다. 외부에서 호출이 가능한 델리게이트는 임의 적으로 호출이 가능하다는 점때문에

프로그램의 상태변화와 상관없이 호출되어 버그를 발생 시킬수 있습니다.


이상으로 델리게이트와 이벤트에 대해서 알아 보았습니다.

수고하셨습니다.

1. 예외 처리 이란 ( Exception Handling)


우리가 인생을 살아가다보면 예상치 못한 일들이 일어나기 마련입니다.

감기에 걸려 출근을 못할수도 있고 물웅덩이에서 물이 튀어 새옷을 버릴수도 있습니다.

이러한 예상치 못한 일들을 겪다보면 자연스럽게 대처하는 방법들이 있습니다.


프로그래밍에서도 마찬가지로 예상치 못한 일들이 일어나기 마련입니다.

더군다나 여러사람이 함께 프로젝트를 진행하다보면 더 많은 일들이 일어나기 마련이지요.

게임을 하던중 예기치 못한 오류가 발생해 프로그램이 종료되는 상황들을 겪어 보셨나요?


이처럼 예외 상황이 발생하였을때 프로그램 종료없이 유연하게 처리하는 것을 예외 처리라 합니다.


2. try~catch, throw, finally


try~catch

앞선 강좌에서 goto 문을 활용하여 어디서든 코드를 점프하여 다음 코드들을 실행하는 것을 접해봤습니다.

이것과 마찬가지로 예외가 발생할 만한 코드들이 있는곳 또는 예외가 발생해서는 안되는 코드에 try 키워드를 

써서 해당 코드들을 블럭( {...} )안에 넣어 놓으면 예외 발생시 예외 상황을 (시도하다) catch 키워드가 있는 곳에서

(받다) 예외상황에 대처할 수 있도록 합니다.


서식 형식

try
{
    //....
}
catch( [예외 객체 1] )
{
}
catch( [예외 객체 2] )
{
}
...


소스코드
using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] arr = { 1, 2, 3 };

            try
            {
                //int[] 배열의 크기를 넘은 인덱스를 사용하여 예외 발생
                arr[4] = 4;
            }
            catch (DivideByZeroException e)
            {
                Console.WriteLine("예외 발생 : {0}", e.Message);
            }
            catch (IndexOutOfRangeException e) // 배열의 크기에 대한 예외 상황 발생시 받는 곳
            {
                Console.WriteLine("예외 발생 : {0}", e.Message);
            }

            try
            {
                //상수를 0으로 나누려하여 예외 발생
                int a = 10;
                a = a / 0;
            }
            catch (IndexOutOfRangeException e)
            {
                Console.WriteLine("예외 발생 : {0}", e.Message);
            }
            catch (DivideByZeroException e) // 0으로 나누기를 하였을때 예외 상황을 받는 곳
            {
                Console.WriteLine("예외 발생 : {0}", e.Message);
            }

            try
            {
                arr[4] = 4;
            }
            //모든 예외상황은 System.Exception 클래스를 상속받아 파생된 클래스입니다.
            //하나의 catch 만으로 모두 처리 할수 있습니다.
            catch(Exception e)
            {
                Console.WriteLine("예외 발생 : {0}", e.Message);
            }
        }
    }
}

결과

예외 발생 : 인덱스가 배열 범위를 벗어났습니다.
예외 발생 : 0으로 나누려 했습니다.
예외 발생 : 인덱스가 배열 범위를 벗어났습니다.


소스코드의 주석의 내용과 같이 모든 예외는 System.Exception으로 부터 파생되어 만들어져 있습니다.

Exception 클래스 만으로 예외상황에 대응 할수 있지만 경우에 따라서는 정확한 처리가 필요할때는 대응이 어려울수

있습니다. 따라서 Exception 만으로 대응 하는것은 자제 하는것이 좋고 올바른 예외 처리를 하기 위해 명확하게 사용하는

것이 좋습니다.


때로는 라이브중인 프로그램에서 최악의 상황(강제 종료)은 피하기위해 예외처리를 설정하고 버그를 수정하기 전까지

유지하는 경우도 있습니다.



throw

catch 키워드로 예외를 받는 다는 것은 어디선가는 예외 상황을 던지고 있다는 말이되고 이는 throw 키워드를 이용하여

직접 예외 상황을 발생 시킬수 있습니다.

다음 소스코드를 참조 하시기 바랍니다.


소스 코드

using System;

namespace ConsoleApp1
{
    class Program
    {
        static int password = 1234;
        static void Main(string[] args)
        {
            try
            {
                CheckPassword(333);
                CheckPassword(1234);
            }
            catch(Exception e)
            {
                Console.WriteLine("예외 발생 : {0}", e.Message);
            }
        }
        static void CheckPassword(int pass)
        {
            if (password != pass)
                throw new Exception("패스워드가 틀립니다.");
            else
                Console.WriteLine("비밀번호 인증완료!");
        }
    }
}

결과

예외 발생 : 패스워드가 틀립니다.


이처럼 프로그래머가 직접 특정 상황에 맞게 예외를 던지고 받을수 있습니다.

 

finally

finally는 try 블럭이 실행된다면 예외가 발생하든 하지 않던 어떤 경우에라도 반듯이 실행되는 키워드 입니다.

할당 받은 100개의 자원들을 사용중에 예외 상황이 발생하여 미처 사용중이던 자원을 해지 하지 못해 사용할수 없는

상황이 발생한다면 사용 할수있는 자원의 갯수는 줄어들고 잠재적 버그를 가지고 사용하게 되는 상황이 발생 할수도

있습니다. 그렇다고 모든 catch 문에서 해지하는 코드를 넣는것 또한 바람직 하지 못한 코드가 됩니다.

이럴때 사용하는 것이 finally 입니다.


소스코드

using System;

namespace ConsoleApp1
{
    class Program
    {
        class NameObject
        {
            bool IsUse = false;
            public bool USE
            {
                get { return IsUse; }
            }
            string sName;

            public NameObject(string name)
            {
                sName = name;
            }
            public void objOpen()
            {
                if (!IsUse)
                {
                    IsUse = true;
                    Console.WriteLine("Open : {0}", sName);
                }
                else
                    throw new Exception("사용중인 오브젝트 " + sName);
            }
            public void objClose()
            {
                if(IsUse)
                {
                    IsUse = false;
                    Console.WriteLine("Close : {0}", sName);
                }
            }
        }
        static void Main(string[] args)
        {
            NameObject[] objs = { new NameObject("A"), new NameObject("B"), new NameObject("C") };
            objs[1].objOpen();

            try
            {
                for (int i = 0; i < objs.Length; i++)
                    objs[i].objOpen();
            }
            catch(Exception e)
            {
                Console.WriteLine("예외 발생 : {0}", e.Message);
            }
            finally
            {
                for(int i = 0; i < objs.Length; i++)
                {
                    if (objs[i].USE)
                        objs[i].objClose();
                }
                Console.WriteLine("모든 오브젝트 해제 완료!");
            }
        }
    }
}

결과

Open : B
Open : A
예외 발생 : 사용중인 오브젝트 B
Close : A
Close : B
모든 오브젝트 해제 완료!


finally는 try를 실행하게되면 어떠한 경우라도 예외가 일어나든 일어나지 않든 간에 반드시 실행됩니다.

위의 예제와 같이 마지막에 해제되는 코드를 넣어 둔다면 자연스럽게 안정성을 가지면서 코드를 정리 할수 있습니다. 


이상으로 예외처리를 마치겠습니다.

수고하셨습니다.





1. 인터페이스 (Interface)


오늘날 우리가 많은곳에서 사용하는 usb 포트는 다방면으로 사용 가능한 장치입니다.

어뎁터를 통해 휴대폰을 충전한다 던지 컴퓨터 주변기기들(마우스, 키보드, 프린터, 선풍기...)을 usb 포트를 통해

전원을 공급한다던지 데이터 수신호를 보낸다던지 컴퓨터와 사용자간 연결 통로같은 역활들을 합니다.

이는 다양한 응용력, 확장능력을 가지고 있습니다.


앞서 말한 usb포트와 같은 역활을 하는것이 이번장에 공부할 인터페이스(Interface)입니다.

인터페이스는 클래스와 비슷해 보이지만 메서드, 이벤트, 프로퍼티, 인덱서 등만 가질수 있다는

차이가 있고 접근 한정자는 public으로만 선언 할수 있습니다. 또한 인스턴스를 만들 수없어 직접적인

사용은 불가능하지만 상속을 받는 클래스의 인스턴스를 생성하는 것은 가능합니다.


- 메서드, 이벤트, 프로퍼티, 인덱서 만을 가질수 있습니다.

- 자체적으로 인스턴스화 될수 없으며 상속을 통한 클래스의 인스턴스는 생성가능 합니다.

- 실체화 될수 없는 인터페이스는 추상적인 멤버를 가집니다.

- 접근 한정자는 public으로만 기본적으로 지정됩니다.

- 인스턴스는 참조자 형으로 사용할수 있습니다.


선언 형식

interface [인터페이스명]
{
     [반환형식] [메서드명] ( [매개변수 목록] );
     //...
}

소스코드

using System; namespace ConsoleApp1 { //인터페이스 메서드의 원형으로만 이루어 집니다. //멤버 변수는 존재 할수 없습니다. interface ITest { //접근제한자 붙이지 않는다. void Function(); void Function2(); } interface ITest2 { void Function2(); } //인터페이스 다중상속 class TestClass : ITest, ITest2 { //상속받은 인터페이스의 모든 메서드를 구현하도록 강제 됩니다. public void Function() { Console.WriteLine("하하하하"); } public void Function2() { Console.WriteLine("호호호호"); } }; class Program { static void Main(string[] args) { TestClass tc = new TestClass(); tc.Function(); tc.Function2(); Console.WriteLine(); //인터페이스는 인스턴스화 될수 없지만 참조자 형으로 사용 가능 ITest interfaceA = tc; interfaceA.Function(); interfaceA.Function2(); Console.WriteLine(); ITest2 interfaceB = tc; interfaceB.Function2(); } } }

결과

하하하하
호호호호


하하하하

호호호호






2. 형변환 ( is, as )


앞서 공부한 클래스에서도 사용할수 있는 키워드 입니다.

형식 비교와 형변환을 위해 사용할수 있는 키워드로 상속 클래스, 상속 인터페이스에서 안전성과 명확성이

필요로 할때 사용할수 있습니다.


is : 객체가 해당 형식인지 비교하여 그 결과를 bool 타입으로 반환 합니다.

as : 형변환 연산자가 변환에 실패할 경우 예외(오류)를 던지는 것에 비해 as는 객체 참조를 null로

     만든다는 차이가 있습니다.


인터페이스가 인터페이스를 상속하고 이를 클래스로 상속받아 인스턴스를 구현

using System;

namespace ConsoleApp1
{
    interface IGun
    {
        void Fire();
    }

    interface IAutoGun : IGun
    {
        void Fire(int count);
    }

    interface ILaser
    {
        void shoot();
    }

    class TestClass : IAutoGun, ILaser
    {
        public void Fire()
        {
            Console.WriteLine("IGun : 빵야!");
        }

        public void Fire(int count)
        {
            for (int i = 0; i < count; i++)
            {
                Console.WriteLine("IAutoGun : 뚜루뚜루!");
            }
        }
        public void shoot()
        {
            Console.WriteLine("ILaser : 찌이이잉~!");
        }

    };

    class Program
    {
        static void Main(string[] args)
        {
            TestClass tc = new TestClass();
            tc.Fire();
            tc.Fire(3);
            tc.shoot();
            Console.WriteLine();

            // is 같은 형식인지 비교
            if(tc is IGun)
            {
               // as : 형변환을 이후 안전성 확인
                IGun gun = tc as IGun;
                if (gun != null)
                    gun.Fire();
            }

            if(tc is ILaser)
            {
                ILaser lasergun = tc as ILaser;
                if (lasergun != null)
                    lasergun.shoot();
            }
        }
    }
}

결과

IGun : 빵야!
IAutoGun : 뚜루뚜루!
IAutoGun : 뚜루뚜루!
IAutoGun : 뚜루뚜루!
ILaser : 찌이잉~!

IGun : 빵야!
ILaser : 찌이잉~!





3. 추상 클래스 (abstract)


추상 클래스는 인터페이스와 클래스중 많은 부분이 클래스에 더 가깝습니다.

메서드, 필드, 메서드 구현, 프로퍼티, 이벤트 등을 가질수 있고 인터페이스와 달리 직접 구현 또한

할수있습니다. 하지만 클래스와 달리 인스턴스는 만들수 없습니다.

이 추상 클래스에서 할수 있는것은 모두 클래스에서 가능한 기능들이지만 왜 굳이 추상 클래스를

사용할까요? 클래스에서 가능하지만 많은 부분을 다른 프로그래머에게 강제할수 없고 설명 또한

필요한 부분이 생길것 입니다. 하지만 추상 클래스는 클래스 자체 만으로 해당 추상 메소드를 강제

할수 있으며 구현되지 않은 부분들은 컴파일러가 알려줄수 있습니다.


- 인터페이스는 명시하지 않은 메서드는 모두 public이 되지만 추상 클래스는 private로 선언됩니다.

- 인터페이스와 달리 직접 구현이 가능하지만 클래스 처럼 인스턴스는 만들수 없습니다.

- 파생 클래스들은 모든 추상 메서드를 구현을 강제 할수 있습니다.

- 추상 클래스가 추상 클래스를 상속한 경우 자식 추상클래스는 부모 추상클래스의 추상 메서드를

  구현하지 않아도 됩니다. (결국 인스턴스를 생성할 클래스에서 구현하기 때문)


소스코드

using System;

namespace ConsoleApp1
{
    //추상클래스 ( 멤버 메서드중 추상 메서드가 하나라도 들어가 있으면 추상 클래스가 됩니다 )
    abstract class Gun
    {
        public int number = 1;

        //발사에 대한 추상적 기능을 강제 할 추상 메서드
        public abstract void Fire();

        //인터페이스와 달리 직접 구현도 가능 합니다.
        public void Reload()
        {
            Console.WriteLine("척커덩!");
        }
    }


    //추상클래스를 상속받은 클래스가 인스턴스화가 가능한 클래스가 되려면 
    //추상함수를 모두 재정의 해야 한다.
    class AutoGun : Gun
    {
        public override void Fire()
        {
            Console.WriteLine("빵야!");
        }
    }

    class LaserGun : Gun
    {
        public override void Fire()
        {
            Console.WriteLine("지이잉~");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Gun g = new AutoGun();
            g.Fire();
            g.Reload();

            if (g is LaserGun)
            {
                LaserGun laser = g as LaserGun;
                if (laser != null)
                    laser.Fire();
            }
            else
            {
                Console.WriteLine("LaserGun 이 없습니다.");
            }
        }
    }
}

결과

빵야!
척커덩!
LaserGun 이 없습니다.




이상으로 인터페이스, 형변환, 추상 클래스의 강좌를 마치도록 하겠습니다.

수고하셨습니다.





1. 구조체 ( struct )


클래스와 닮은면이 많은 구조체는 struct 키워드를 사용하여 선언합니다.

하지만 클래스는 실세계의 객체를 추상화하려는데 그 목적이 있지만 구조체는 데이터만을 위한 데이터 집합체로

목적성이 다릅니다. 또한 클래스와 다르게  은닉성 자체를 강요하지 않으며 편의를 위해 공개적으로 메서드를 

사용하는 경우가 많으며 인스턴스 자체도 스택에 할당되 메서드 호출이 완료되고 메모리에서 사라지게 됩니다.

가비지 컬렉터를 귀찮게 하지 않는 점과 인스턴스 사용이 끝나면 즉시 메모리에서 해제 된다는점 이러한 장점으로

클래스와 비슷하지만 충분히 사용할 가치가 높은 복합 데이터 형식입니다. 


※ 메모리 영역 - 데이터 영역(Data), 힙(Heap), 스텍(Stack)

    *데이터 영역 : static 같은 전역 변수들이 할당되며 프로그램 종료시 해제 됩니다.

    *힙 : class 같은 복합 데이터 형식이 동적으로 메모리에 할당하고 참조자가 끊어지면 가비지 콜렉터에 의해

          자동(해제 시점은 알수 없음)으로 해제 됩니다.

    *스택 : struct와 같은 데이터 형식이 메서드를 호출시 지역변수와 매개변수가 생성되는 곳으로 메서드의

          호출이 끝나면 해제됩니다.  



특징 

클래스 

구조체 

키워드

class

struct

인스턴스 생성

new 키워드로 인스턴스 생성

new 또는 선언 만으로 생성

메모리 영역

힙(Heap)

스텍(Stack)

 복사

얕은 복사

깊은 복사

형식

참조

상속

가능

불가능



선언 형식

struct [구조체 이름]
{
    // 필드
    // 메서드
}


소스 코드

using System;

namespace ConsoleApp1
{
    class Program
    {
       struct TestStruct
       {
            //코드에 명시적으로 초기화 불가능
            //public int number = 10;		
            public int number;

            public void Show()
            {
                Console.WriteLine("Number : {0}", this.number);
            }

            /*
            //구조체는 매개변수가 없는 기본생성자를 재정의 할수 없습니다.
            public TestStruct()
            {
                this.number = 0;
                Console.WriteLine("기본생성자 실행");
            }
            */

            //매개변수가 있는 생성자는 정의 가능 하지만
            //생성자 내에서 모든 맴버 변수들이 초기화 되어야 합니다.
            public TestStruct(int a)
            {
                this.number = a;
            }
        }

        static void Main(string[] args)
        {
            //선언만으로 생성시 지역 변수의 값 할당이 필요
            TestStruct testStruct;
            testStruct.number = 15;
            testStruct.Show();

            //new 만들어진 구조체의 특징
            //new 키워드가 구조체에서 동작하는 방식은 동적 할당되는 개념이 아니고
            //기본 생성자로 초기화 시키는 개념입니다.
            TestStruct testStruct1 = new TestStruct();
            testStruct1.number = 100;
            testStruct1.Show();
            Console.WriteLine();

            //깊은 복사(클론 생성)
            TestStruct testStruct2 = testStruct;
            testStruct2.number = 777;

            testStruct1.Show();
            testStruct2.Show();
            Console.WriteLine();

            //new 키워드로 생성할때 새롭게 정의한 생성자를 선택할 수 있는 기능이 있습니다.
            TestStruct testStruct3 = new TestStruct(-400);      
            testStruct3.Show();
            Console.WriteLine();

            //구조체 배열에 대한 예
            TestStruct[] testStructArr = {
                                             new TestStruct(88),
                                             new TestStruct(20),
                                             new TestStruct(55)
                                         };

            for (int i = 0; i < testStructArr.Length; i++)
            {
                testStructArr[i].Show();
            }
        }
    }
}

결과

Number : 15
Number : 100

Number : 100
Number : 777

Number : -400

Number : 88

Number : 20

Number : 55


소스 코드내에 주석( // )으로 구조체의 기본적인 사용 방법과 특징을 적어 놓았습니다.




2. 오퍼레이터(operator)


오퍼레이터는 정적 멤버 메서드를 정의하여 연산자(+, -, *, /....)를 오버로드할 수 있는 기능입니다.

operator키워드로 선언되며 여러 연산자를 정의 할수 있습니다.


사용 형식

public static [구조체명 반환형식] operator [연산자]([구조체명 매개변수]...)
{
   //...
   return [구조체명 반환형식];
}


연산자 

기호 

오버로드 가능성 

 단항 연산자

 +, -, ~, ++, --, true, false

 가능

 이항 연산자

 +, -, *, /, %, &, |, ^, <<, >>

 가능

 비교 연산자

 ==, !=, <, >, <=, >=

 조건부 가능  비교 연산자는 가능 하지만 다음에

 오는 참고 사항을 참조 )

 조건부 논리 연산자

 &&, ||

 불가능 (오버로드 가능한 & 및 | 를 사용하여 계산 )

 할당 연산자

 +=, -=, *=, /=, %=, &=, |=, ^=, <<=,>>=

 불가능 ( 이항 연산자가 오버로드 되면 

            암시적으로 오버로드 됨)

 기타

 =, ., ?:, ??, ->, =>, f(x), as, new, is ... 등등

 불가능 (키워드)


소스코드

using System;

namespace ConsoleApp1
{
    class Program
    {
        struct Vector2
        {
            public float x;
            public float y;

            public Vector2(float x, float y)
            {
                this.x = x;
                this.y = y;
            }

            //구조체도 object 를 상속받는다.
            public override string ToString()
            {
                //Console.WriteLine() 메서드의 기본 원리는 출력 할수 있는 형식들을
                //ToString() 메서드를 이용하여 문자열로 만들어 출력합니다.
                //새롭게 정의한 구조체 형식을 출력하기 위해 오버로드하여 사용하였습니다.
                return string.Format("{0:F2}, {1:F2}", this.x, this.y);
            }

            //Vector2 끼리 덧셈연산이 가능하게 한다.
            public static Vector2 operator +(Vector2 lhs, Vector2 rhs)
            {
                Vector2 nv = new Vector2();
                nv.x = lhs.x + rhs.x;
                nv.y = lhs.y + rhs.y;

                return nv;
            }
        }

        static void Main(string[] args)
        {
            Vector2 vecPosition = new Vector2(19.2f, 3.5f);
            Console.WriteLine(vecPosition);

            // + 이항연산자가 오버로드 되면 대입 연산자도 암시적으로 오버로드되어
            // += 연산자를 사용할수 있습니다.
            vecPosition += new Vector2(0.0f, 60.0f);	
            Console.WriteLine(vecPosition);
        }
    }
}

결과

19.20, 3.50
19.20, 63.50


구조체는 기본적으로 object를 상속받아 구현되었습니다.

결과 출력을 위해 강좌 8편의 override 기능을 사용해 ToStoring() 메서드를 재정의 하였습니다.

자세한 코드설명은 주석을 참고하여 보시면 되겠습니다.


다음 강좌에서는 인터페이스(Interface), 형변환(is, as), 추상 클래스(abstract) 대해 알아 보도록 하겠습니다.

그럼 수고하셨습니다.






1. 분할 클래스( Partial Class )


프로그래밍을 하다보면 클래스를 구성하는 코드들이 엄청나게 늘어나 있는 경우가 있습니다.

또는 다른 팀원과 나누어서 작업을 진행할때 여러개의 소스 파일로 분할하여 동시에 작업하거나

작업자를 분할하여 관리와 편의를 위해 분할하여 사용 합니다.


선언 형식

...
    partial class [클래스명]
    {
          //
    }
 ...

단순히 class를 만들때 partial 키워드만 추가 시켜주면 됩니다.


소스코드

using System;

namespace ConsoleApp1
{
    partial class TestGun
    {
        public void Fire()
        {
            Console.WriteLine("탕!");
        }
    }
    partial class TestGun
    {
        public void Fire2()
        {
            Console.WriteLine("두!두!두!");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            TestGun gun = new TestGun();
            gun.Fire();
            gun.Fire2();
        }
    }
}

결과

탕!
두!두!두!


컴파일러 입장에서는 분할하여 클래스를 제작하여도 컴파일시 하나로 보기 때문에 프로그래머의 관리와 편의가

주된 목적으로 사용됩니다.



2. 확장 메서드 ( Extension Method)


확장 메소드는 클래스의 기능을 확장하는 방법입니다.

쉽게말해 우리가 using키워드를 사용하여 다른 네임스페이스의 기능들을 가져다 쓰듯이 공통적이나

일반적으로 쓸수있는 기능들을 모아 놓는 방식으로 수학적인 연산 기능이나 데이터를 섞어 준다거나

하는 기능들을 넣을수 있습니다.


선언형식

namespace [네임스페이스명]
{
    public static class [클래스명]
    {
        public static [반환형식] [메서드명] ( this [확장 대상 형식], [매개변수들])
        {
              //
        }
        ...
    }
}

기본적으로 확장 메서드는 정적(static) 클래스 내에 정적 메서드로 정의 됩니다.

그리고 메서드의 첫번째 매개변수는 this 키워드로 한정되고 반듯이 있어야 합니다.


소스 코드

using System;
using Extension;

namespace Extension
{

    public static class IntExtension
    {
        public static int Square(this int width, int height)
        {
            return width * height; ;
        }
    }
}

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("{0}", 12.Square(8));
        }
    }
}

결과

96


이러한 확장 메서드의 장점은 이미 빌드된 라이브러리를 참조하여 사용할때 수정이나 추가를 하고 싶어도

코드내용을 알수없어 직접적인 수정이 불가능 합니다. 이러할때 기능적인 부분을 좀더 확장 시켜 사용하고 싶다면

확장 메서드를 활용해보세요.


이상으로 클래스강좌는 마무리하고 구조체(struct) 및 오퍼레이터(operator)에 대해 배워보도록 하겠습니다.


+ Recent posts