1. 프로퍼티(Property)


우리는 지난 강좌에서 클래스 내부의 필드나 메소드에 마음것 접근하여 호출하고 사용할수 있었습니다.

하지만 이것은 객체 지향 프로그래밍에서 위험한 결과를 초래할수 있습니다.

정보의 은닉은 객체 지향 프로그래밍에서 중요한점인데 외부에서 클래스 내부의 정보를 마음것

수정할수 있다는 것은 버그를 발생하게 하고 안정성을 보장받지 못합니다.


클래스는 수십 수백 개의 필드나 메서드를 가질수 있습니다. 이러한 정보들을 외부에 모두 보여지게

한다면 단순히 혼자 프로젝트를 만드는 것이 아니라 여러명이 한 프로젝트를 만들때 불필요한 정보 노출과

혼선을 야기시킬수 있습니다. 수 많은 필드와 메서드들이 한 곳에 모여서 서로 정보를 노출 시킨다고 상상해

보세요 얼마나 재앙적이고 절망적인 상황이 발생 할지 그래서 외부의 접근을 제한하고 필요한 정보만

외부에 공개할 필요가 있습니다.


결국 우리가 필요한건 정보 은익안전성 확보입니다.


c#에서는 이러한 문제를 해결하기 위해 프로퍼티라는 기능을 제공합니다.

get, set 키워드를 이용하여 필드의 값을 수정할수도 받을수도 있습니다. 반드시 두개를 같이 사용

해야 하는것은 아니고 어느하나만 사용해도 무방합니다.


기본 선언 형식

class 클래스명
{
     [변수 타입] [변수명];
     [접근한정자] [변수 타입] 프로퍼티명
     {
           get
           {
                return [변수명];
            }
           set
            {
                   [변수명] = value;
             }
      }
}

프로퍼티는 필드와 메서드의 기능을 모두 담당 할수있습니다.

예제 코드로 확인 해보도록 하겠습니다.

using System;

namespace ConsoleApp1
{
    class Program
    {
        class Test
        {
            private int nNum;

            public int NUMBER
            {
                get
                {
                    return nNum;
                }
                set
                {
                    nNum = value;

                    if (nNum > 100)
                        nNum = 100;
                    else if (nNum < -100)
                        nNum = -100;
                }
            }

            public Test(int a)
            {
                nNum = a;
            }
            public void Show()
            {
                Console.WriteLine("nNum : {0}", nNum);
            }
        }

        static void Main(string[] args)
        {
            Test testA = new Test(88);
            testA.Show();

            testA.NUMBER = 10000;
            testA.Show();

            int nGetNumber = testA.NUMBER;
            Console.WriteLine(nGetNumber);
        }
    }
}

결과

nNum : 88
nNum : 100
100


위의 코드 17줄을 보시면 set의 코드블럭 안에서 100 이상 -100 이하의 값은 받을수 없도록 하여 메서드 처럼

사용하였습니다. 이는 get에서도 가능하며 실제 main 메서드에서 사용한것과 같이 프로퍼티에 변수에 값을

설정하는 것처럼 사용할수도 값을 얻어올수도 있습니다.




2. Class 상속 (virtual, override)


class에서는 상속이라는 기능이 존재 합니다. 쉽게 말해 부모 자식 관계라고 보통 말하는데 

부모 클래스에서 virtual 키워드를 이용하여 이 메서드는 자식 클래스에서 재정의 할수 있도록

하겠다는 것을 의미하고 override 키워드는 자식 클래스에서 virtual로 선언된 메서드를 재정의

하겠다고 알리는 것입니다.


기본 선언 형식

class [부모 클래스명]
{
    virtual [한정자] [반환타입] [메서드명]()
    {
        //
    }
}
class [자식 클래스명] : [부모 클래스명]
{
    override [한정자] [반환타입] [메서드명]()
    {
    }
}


virtual, override 키워드 없이도 상속이 가능합니다.

재정의가 필요한 경우나 재정의가 필요 없는경우도 상속하여 사용 할수 있습니다.

base키워드는 부모의 메서드를 호출할때 사용합니다.

using System;

namespace ConsoleApp1
{
    class Program
    {
        class parent
        {
            virtual public void show()
            {
                Console.WriteLine("부모 클래스 show 메서드 호출!");
            }
        }
        class child : parent
        {
            public override void show()
            {
                Console.WriteLine("자식 클래스 show 메서드 호출!");
            }
        }
        class child2 : parent
        {
            override public void show()
            {
                base.show();
            }
        }
        static void Main(string[] args)
        {
            parent parentA = new parent();
            parentA.show();

            child childA = new child();
            childA.show();

            child2 childB = new child2();
            childB.show();
        }
    }
}

결과

부모 클래스 show 메서드 호출!
자식 클래스 show 메서드 호출!
부모 클래스 show 메서드 호출!


위의 코드를 보면 경우에 따라 재정의를 할수도 있고 재정의 하지않고 부모의 메서드를 그대로 사용할수도 있습니다.

또는 부모의 메서드와 같이 사용할수도 있겠지요.

또한 눈치 채셨나요? 앞서 배운 for문이나 whilie문 같이 class역시 중첩하여 다중으로 사용할수 있습니다. 이는 

parent, child, child2 클래스들은 Program클래스 내에서만 사용하기 위해 만든것 입니다. 

외부에서는 절대 저 클래스들이 무엇인지 알수 없게 됩니다.


클래스의 다양성

업캐스팅(Upcasting) : 자식 클래스가 부모 클래스로 형변환

다운캐스팅(Downcasting) : 부모 클래스가 자식 클래스로 형변환


업캐스팅과 다운캐스팅은 잘사용하면 좋은 효과를 얻을수 있지만 명확하지 않은 상황에서는 버그를 발생 시킬수 있습니다.

using System;

namespace ConsoleApp1
{
    class Program
    {
        class phone
        {
            virtual public void Call()
            {
                Console.WriteLine("전화를 걸다");
            }
        }
        class android : phone
        {
            public void ApkRead()
            {
                Console.WriteLine("apk 앱설치");
            }
        }
        class iphone : phone
        {
            public void iosRead()
            {
                Console.WriteLine("ios 앱설치");
            }
        }

        static void Main(string[] args)
        {
            android AndroidPhone = new android();
            phone myPhone = AndroidPhone;           //업캐스팅
            ((android)myPhone).ApkRead();           //다운캐스팅

            //예외 발생
            //((iphone)myPhone).iosRead();

            //빌드는 되지만 실행시 예외 발생
            //iphone secendPhone = (iphone)myPhone;  //다운캐스팅
            android secendPhone = (android)myPhone;
            secendPhone.ApkRead();
        }
    }
}

결과

apk 앱설치
apk 앱설치


위의 코드는 개념적으로 안드로이드 폰(자식)을 그냥 폰(부모)이라 부를수 있지만 아이폰이라 부를 수는 없을 것입니다.

또한 폰이라 부르지만 우리는 이것이 안드로이드 폰이라는 것을 알고 있기에 안드로이드 기능(메서드)를 사용함에 있어서도

아무런 문제가 발생되지 않습니다. 하지만 폰을 아이폰 케이스만 입힌다고 아이폰이 될수 없는 것 처럼 아이폰이라 컴파일러를

속일수는 있어도 기능을 사용한다면 예외가 발생합니다.


오버라이딩 봉인

sealed : 파생된 클래스에서 오버라이딩한 메서드에 이 키워드(sealed)를 같이 사용하면 더이상 하위 클래스에서 

           오버라이딩 할수 없습니다.


[virtual] ---> [override sealed] -X-> [override]


...
    virtual public void show()
        {
            Console.WriteLine("부모 클래스 메서드 호출!");
        }
    }
    class child : parent
    {
        override sealed public void show()
        {
            Console.WriteLine("자식 클래스 메서드 호출!");
        }
    }
    class grandchild : child
    {
       override public void show()
        {
            Console.WriteLine("손자 클래스 메서드 호출!");
        }
    }
...


이렇게 컴파일러에서 빌드가 되지 않도록 에러가 발생 됩니다. 왜 이렇게 봉인을 하는것 일까요?

이것은 프로그래머의 배려로 혹시나 오작동을 할 위험이 있는 메서드나 잘못 오버라이딩함으로써 발생할수 있는

오류를 미리 방지하기 위함입니다.


다음 강좌에서는 분할 클래스, 확장 메서드에 대해 알아보도록 하겠습니다.

수고하셨습니다.



1. 클래스(Class)


클래스를 알아보기에 앞서 우리는 객체(Object)에 대해 알아야 될 필요가 있습니다.

서점이나 인터넷에서 프로그래밍 관련 서적 강좌들을 보면 항상 나오는 말이 객체 지향 프로그래밍

(Object Oriented Programming)이라는 OOP를 언급합니다. 도대체 객체란 무엇일까요?

쉽게 말해 우리가 인지하고 사용하고 보고 듣고 하는 모든 것을 지칭하는 단어 입니다.

영화 메트릭스에서 가상세계를 구성하는 모든 것을 객체라 할수 있고 이 객체들은 각각 유기적으로

행동하고 움직이고 구성됩니다. 이런 포괄적인 의미의 객체는 속성과 기능을 가지고 있습니다.

예를 들자면 동물의 속성은 종, 크기, 성격, 색깔 등으로 나눌수 있고 기능은 걷기, 먹기, 보기, 듣기

등으로 볼수 있습니다.


프로그래밍에서 보자면 속성은 '데이터' 기능은 '메서드'로 표현 할수 있습니다.

추운 겨울날 우리가 사먹는 붕어빵을 만들기 위해서는 많은 속성들이 필요합니다.

밀가루 반죽, 팥 앙금, 익히기 위한 불, 이러한 것 들이 모여 붕어빵이 됩니다.

이때 여러개의 붕어빵을 만들기 위한 틀을 클래스라 할수 있고 붕어빵은 객체라 볼수 있습니다.


2. 실습


class 구조

[접근 제한자] class [클래스 이름]
{
    //메서드, 데이터...
}

접근 제한자는 메서드를 배울때와 동일한 키워드(public, priavte...)를 사용합니다.

접근 제한자 키워드가 없다면 c#에서는 private로 선언됩니다.

소스코드

using System;

namespace ConsoleApp1
{
        class hero
        {
            public string sName;
            public int nHP;
            public int nPower;

            public void Attack(string target)
            {
                Console.WriteLine("{0}에 {1}의 데미지를 주었습니다.", target, nPower);
            }
        }

        static void Main(string[] args)
        {
            hero newHero = new hero();
            newHero.sName = "맥스";
            newHero.nHP = 10;
            newHero.nPower = 2;

            Console.WriteLine("이름 : {0}, 체력 : {1}, 공격력 : {2}", newHero.sName, newHero.nHP, newHero.nPower);
            newHero.Attack("허수아비");
        }
}

결과

이름 : 맥스, 체력 : 10, 공격력 : 2
허수아비에 2의 데미지를 주었습니다.


우리는 hero 라는 클래스를 만들었습니다. 이 클래스는 이름, 체력, 공격력이라는 데이터를 가지고 있고 공격이라는 메서드를

사용할수 있습니다. 완벽한 hero의 클래스는 아니지만 이렇게 추상화(중요한 내용에만 중점을 두고 이를 간략화 시킨 것)하여

다양한 hero들을 만들어 낼수 있습니다.


우리가 만든 클래스는 생성을 하여 데이터들을 초기화 해주고 사용하여야 합니다. 추상화된 클래스는 메모리에 데이터들이 들어갈

공간을 확보만 하고 데이터들은 없기 때문에 바로 사용한다면 에러가 발생합니다.

우선 우리가 주목할 코드는 클래스의 생성 코드입니다.


소스코드 19줄을 보면 new라는 키워드가 등장 합니다. 이것은 객체를 새롭게 생성할때 마다 사용되는 키워드로

객체 생성 방법은 다음과 같습니다.


[클래스명] [사용할 이름] = new [클래스명]();


생성된 객체는 닷(.)을 통해 public 필드(데이터)에 접근할수 있습니다.

이렇게 각 데이터들을 호출하여 값을 초기화 또는 변경을 하여 사용할수 있습니다.



생성자, 소멸자

우리가 만든 클래스는 생성자가 없더라도 컴파일러에서 자동적으로 기본적인 생성자를 제공합니다. 만약 직접 생성자를

클래스에서 만들었다면 컴파일러는 기본 생성자를 지원 하지 않습니다.


생성자 : 객체 생성시 호출되는 메소드

소멸자 : 객체 소멸시 호출되는 메소드


이 시대를 살아가면서 우리는 흑수저, 금수저라는 말을 씁니다.

사람 또한 객체이며 사람이 태어날때 생성자가 호출된 시기이고 사람이 죽었을때 소멸자가 호출되는 것과 마찬가지 입니다.

같은 객체지만 무엇인가를 가지고 생성되기도 하고 무엇인가를 남기고 소멸되기도 하는데 이와 같은 맥락으로

우리는 생성할때 무엇인가를 줄수도 소멸할때 무엇인가를 남길수도 있습니다.


컴퓨터가 가지는 자원은 유한하며 이때문에 자원을 유지시키고 안정시키는데 프로그래머는 많은 공을 들입니다.

이렇게 최적화를 하고 안정성을 높이기위해 생성자와 소멸자를 이용하기도 합니다.


생성자 구조

class [클래스명]
{
    [접근 제한자] [클래스명] ( 매개변수 )
    {
        //
    }
    // 필드
    // 메서드
}

생성자의 이름은 클래스의 이름과 동일하고 매개변수를 받을수 있으며 특정 반환형식을 사용할수 없습니다.


소멸자 구조

class [클래스명]
{
    ~[클래스명] ()
    {
        //
    }
}

생성자와 마찬가지로 소멸자도 클래스 이름과 동일하지만 이름 앞에 ~기호를 붙여 사용합니다.


소스코드

using System;

namespace ConsoleApp1
{
    class TestClass
    {
        private string sName;
        public TestClass(string name)
        {
            sName = name;
            Console.WriteLine(sName + " 생성!");
        }
        ~TestClass()
        {
            Console.WriteLine(sName + " 소멸!");
        }
    }

    static void Main(string[] args)
    {
        TestClass testA = new TestClass("A 객체");
        TestClass testB = new TestClass("B 객체");
        TestClass testC = new TestClass("C 객체");
    }
}

결과

A 객체 생성!
B 객체 생성!

C 객체 생성!

C 객체 소멸!
B 객체 소멸!
A 객체 소멸!


위의 소스코드는 기본적인 객체의 생성과 소멸을 보여줍니다. 여기서 특이한 점은 A, B, C 순서로 생성 되었지만

소멸시에는 C, B, A 순으로 소멸되었습니다. 이는 c#에서 지원하는 메모리 반환인 가비지 컬렉터가 실행된 결과입니다.


가비지 컬렉터

c나 c++ 같은 언어에서는 프로그래머가 언제 메모리를 반환할지에 대한 제어가 가능합니다.

c#은 생산성과 안정성을 높이기위해 다른 언어에서 제어가 가능했던 메모리 반환에 대해 가비지 컬렉터를 지원

하게 되었습니다. 메모리에 대한 제어는 많은 오류를 일으키고 생산성을 저하시키는 단점이 있었는데 이를 보완

한것입니다. 하지만 가비지 컬렉터 또한 만능은 아닙니다. 위의 코드 결과에서 보듯이 가비지 컬렉터는

언제 어떻게 어는 시점에 실행될지 아무도 모르기 때문에 소멸자를 쓰는 이점이 사라집니다.


c#에서는 이러한 가비지 컬렉터 때문에 프로그래머가 제어하기 어렵고 무분별한 소멸자 사용은

성능 저하를 일으켜 되도록이면 소멸자는 사용하지 않는것이 좋습니다.



3. 심화 학습


1. 생성자는 오버로딩이 가능하며 멤버 변수(필드)에 명시적으로 초기화가 가능합니다.

2. 초기화 되지 않은 멤버 변수는 기본적으로 0으로 초기화 됩니다.

3. 클래스에서는 this라는 키워드를 지원합니다.

   this는 자기 자신을 뜻 하고 자신의 인스턴스에 대한 참조를 의미 합니다.

4. new로 생성된 클래스 인스턴스는 메모리의 Heap 영역에 할당이 됩니다.

   이 Heap 영역에 있는 인스턴스에 대한 참조가 끊기게 되면 이는 가비지(쓰레기 데이터)가 됩니다.

5. 얕은 복사와 깊은 복사

   클래스는 태생이 참조형식 이어서 같은 클래스 끼리의 복사는 얕은 복사(메모리 주소)가 일어나고

   깊은 복사(값에 의한 복사)는 다른 인스턴스를 만들어야 합니다.

6. 임의적으로 가비지 콜렉터를 호출하였지만 호출시점과 실행시점이 다릅니다.


아래 테스트 코드를 통해 위의 내용들을 확인해 보겠습니다.



소스코드

using System;

namespace ConsoleApp1
{
        class Test
        {
            public int nNum1 = 100;
            public int nNum2;

            string sName = "객체";

            public Test()
            {
                Console.WriteLine("Test() 실행");
            }
            //1. 생성자의 오버로딩
            public Test(string name)
            {
                sName = name;
                Console.WriteLine(sName + " 생성!");
            }

            //3. 이름이 동일한 변수이지만 this 키워드를 사용하여
            //   자기 자신의 인스턴스에 접근
            public Test(int nNum1, int nNum2)
            {
                this.nNum1 = nNum1;
                this.nNum2 = nNum2;
            }
            ~Test()
            {
                Console.WriteLine("{0} 소멸!", sName);
            }

            public void Show()
            {
                Console.WriteLine("{0} nNum1 : {1}, nNum2 : {2}", sName, nNum1, nNum2);
            }

            //5. 깊은 복사
            //새로운 인스턴스를 생성해 클론을 만듭니다.
            public Test GetClone()
            {
                Test newInstance = new Test("클론 객체");
                newInstance.nNum1 = this.nNum1;
                newInstance.nNum2 = this.nNum2;

                return newInstance;
            }
        }

        static void Main(string[] args)
        {
            //2. 초기화 되지 않은 멤버 변수
            Test testA = new Test("A 객체");
            testA.Show();

            
            Test testB = new Test(1, 3);
            testB.Show();

            //4. 참조가 끊겨 가비지가 되었음
            testB = null;       

            //6. 가비지 콜렉터 호출
            System.GC.Collect();
            Console.WriteLine();
            Console.WriteLine("가비지 컬렉터 호출.....");
            Console.WriteLine();

            Console.WriteLine("얕은 복사");
            Test testC = testA;
            testC.nNum1 = -10;
            testC.nNum2 = -30;
            testA.Show();       //testC의 필드에 값을 할당했지만 testA의 값이 변경되었음
            testC.Show();
            Console.WriteLine();

            Console.WriteLine("깊은 복사");
            testC = testA.GetClone();
            testC.nNum1 = 88;
            testC.nNum2 = 99;
            testA.Show();
            testC.Show();
            Console.WriteLine();
        }
}

결과

A 객체 생성!
A 객체 nNum1 : 100, nNum2 : 0
객체 nNum1 : 1, nNum2 : 3

가비지 컬렉터 호출.....

얕은 복사
A 객체 nNum1 : -10, nNum2 : -30
A 객체 nNum1 : -10, nNum2 : -30
객체 소멸!

깊은 복사
클론 객체 생성!
A 객체 nNum1 : -10, nNum2 : -30
클론 객체 nNum1 : 88, nNum2 : 99

클론 객체 소멸!
A 객체 소멸!


디버그 모드로 주석과 같이 확인 하시면 소스코드를 이해하는데 도움이 됩니다.

main 안의 코드를 확인해보면  다음과 같습니다.


1. testA의 객체를 생성하고 필드값을 출력

2. testB의 객체를 생성하고 필드값 초기화 후 출력

3. testB 객체에 대한 참조 끊기

4. 가비지 콜렉터를 호출

5. testC는 testA의 값을 복사 ( 얕은 복사로 testA의 참조만 복사 되었음)

6. testC의 값을 변경 ( testC의 값을 변경하였으나 testA의 값이 변경)

7. 4번에서 부른 가비지 콜렉터가 이제 실행

8. testC에 testA의 복사를 다시 시도 GetClone() 메서드를 호출하여 인스터스 생성후 복사

9. 깊은 복사가된 testC의 필드 값을 변경후 두 객체를 출력


이 예제코드로 심화 학습에서 언급한 내용들을 확인해 볼수 있었습니다.

Class는 앞으로도 몇번에 걸쳐 강좌에서 다루도록 하겠습니다.


복잡하고 긴 글 학습하시느라 수고하셨습니다.

1. 목표


메서드의 이해 및 사용


2. 정의


메서드(Method)란 C, C++ 언어에서는 함수(Function)불렸고 약간의 차이는 있겠지만 큰 틀에서 보면 같은 것을 지칭합니다. 

c#에서는 메서드로 불리고 한글표기로는 메서드가 맞지만 많은 사람들이 메소드라고 사용하기도 합니다.


메서드는 어떠한 동작을 하는 코드들을 하나의 이름으로 묶어 놓은것이고 이 이름을 호출하여 실행 할수 있습니다.

예) 우리가 앞서 배운 출력을 하기 위한 메서드는 Console.WriteLine();



3. 실습


선언 방식

class 클래스 이름
{
    [한정자] [반환 형식] [메서드 이름] ( [매개 변수] )
    {
         //실행 코드
         return [반환 형식 결과];
    }
}

한정자

여기서는 액세스 한정자(public, protected, private)에 대해서만 다루도록 하겠습니다.

액세스 한정자는 말 그대로 우리가 만드는 메서드를 호출할수 있는 권한을 정해 놓는 것입니다.

c#에서는 한정자가 없다면 자동으로 private로 지정됩니다.


public : 공개(누구나 접근하여 사용)

protected : 일부 공개(자신이 만들어진 class 또는 이를 상속 받는 하위 class에서만 사용)

private : 비공개(자신의 class에서만 사용)


반환 형식

void, int, string, class...등 이 메서드의 데이터 반환 형식을 지정할수 있습니다.

반환 형식은 선언된 반환 형식과 동일합니다.


void  : 아무것도 반환 할게 없는 경우 return문도 필요하지 않습니다.

int : 코드를 실행하고 마지막에 return뭉으로 int형식의 반환값을 보냅니다.



매개 변수

메서드의 마지막에 붙는 작은 괄호 ( ) 안에 실행 코드에서 사용 될 특정한 데이터를 받을수 있습니다.

매개 변수는 없을수도 여러개 일수도 있습니다.


static

static은 정적메소드로 프로그램이 처음 실행될때 메모리에 자동적으로 생성이 됩니다.

그래서 프로그램의 시작점(main(string[] args))인 main 메서드 또한 static으로 선언되어 있는데

이 정적메소드에서 사용하기위해 예제 및 강좌 에서는 static 한정자를 붙여 사용합니다.


소스 코드

using System;

namespace ConsoleApp1
{
    class Program
    {
        static int intPlus (int a, int b)
        {
            int result = a + b;
            return result;
        }
        static void Main(string[] args)
        {
            int nPlus = intPlus(10, 20);
            Console.WriteLine(nPlus);
        }
    }
}

결과

30 


위의 코드에서는 intPlus 메서드를 이용하여 int형 매개변수를 두개를 받아들이고 두 매개변수의 합을

리턴하는 메서드를 만들었습니다.

코드의 흐름을 알고 싶으시면 강좌 4편에서 언급한 디버그모드를 활용하여 확인하실수 있습니다.

14번 줄에 중단점을 놓고 F11을 이용해 다른 메서드로 이동하는 것까지 추적하여 볼수 있습니다.



오버로딩

우리가 Console.WriteLine의 메서드를 사용할때 int형이나 string과 같이 여러가지의 매개변수를

넣어 사용 하였는데 만능 메서드처럼 모든 매개변수를 받아들이는 것을 보았습니다.

이는 같은 이름의 메서드지만 매개변수로 받는 인자값을 달리하여 각각 메서드들이 만들어져 있고

실제로 사용할때 같은 메서드지만 매개변수의 형식에 따라 각각 대응을 하게 되는것을

메서드 오버로딩이라 합니다.



소스코드

using System;

namespace ConsoleApp1
{
    class Program
    {
        static int Plus (int a, int b)
        {
            int result = a + b;
            return result;
        }
        static float Plus(float a, float b)
        {
            float result = a + b;
            return result;
        }

        static void Main(string[] args)
        {
            int nPlus = Plus(10, 20);
            Console.WriteLine(nPlus);

            float fPlus = Plus(1.4f, 2.423f);
            Console.WriteLine(fPlus);

        }
    }
}

결과

30
3.823


위의 예제 처럼 같은 Plus 메서드 이지만 매개변수를 받는 인자값을 다르게 하여 int형과 float형에

대응 하도록 하였습니다.




매개 변수 전달 방식

매개 변수의 전달 방식은 크게 두가지로 나눌수 있는데 값에 의한 전달(call by value)과

참조에 의한 전달(call by reference)이 있습니다.


쉽게 말해 call by value는 메서드에서 값을 복사하여 전달 받아 사용은 할수 있지만 값의 변경은

하지못하고 call by reference는 해당 값이 있는 주소를 참조하여 전달 받아 값의 변경도 가능한

방법입니다.


소스코드(ref)

using System;

namespace ConsoleApp1
{
    class Program
    {
        //call by value
        static void Plus (int a)
        {
            a += 323;
        }
        //call by reference
        static void Plus(ref int a)
        {
            a += 814;
        }

        static void Main(string[] args)
        {
            int nA = 17;

            Plus(nA);
            Console.WriteLine("nA : {0}", nA);

            Plus(ref nA);
            Console.WriteLine("nA : {0}", nA);
        }
    }
}

결과

nA : 17
nA : 831


위의 예제코드에서 call by value는 8번줄의 메서드 처럼 인자 값 앞에 특정 키워드를 사용하지 않고 변수 형식과

변수이름을 넣어 사용하고 call by reference는 13번줄의 메서드처럼 인자 값 앞에 ref라는 키워드를 붙여 사용합니다.

이는 메서드를 호출하여 인자값을 전달 할때도 동일하게 ref 키워드를 짝으로 사용합니다.


call by reference의 키워드는 ref, out, in 세가지가 있습니다. 차이점은 앞서 사용한 ref는 매개 변수를 넘겨주고 받을때 먼저

초기화 해서 사용 하여야 되고 out은  초기화 하지 않아도 사용할수 있습니다. 이때 ref와 동일하게 out도 매개 변수를

넘겨주는 쪽과 받는쪽 둘다 out라는 키워드를 사용해야 합니다.

마지막으로 in은 ref와 out과 참조를 통해 인수를 전달하지만 값에 대한 수정은 불가능 합니다. 이는 매개변수의 안정성을

유지하면서 메서드를 사용할때 매개변수들의 복사본을 만들지 않아 잠재적으로 성능향상에 도움이 됩니다.

※이미 ref, out, in의 참조 형식의 메서드가 오버로드 되어 있는 경우 중복하여 오버로드 하여 사용할수 없습니다.


소스코드(out)

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Plus (out int a)
        {
            a = 777;
        }

        static void Main(string[] args)
        {
            int nA;

            Plus(out nA);
            Console.WriteLine("nA : {0}", nA);
        }
    }
}

결과

nA : 777


소스코드(in)

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Plus (in int a)
        {
            //아래와 같이 값에 대한 수정은 오류발생
            //Uncomment the following line to see error CS8331
            //a = 19;

            Console.WriteLine(a + 111);
        }
        static void Main(string[] args)
        {
            int nA = 777;

            Plus( nA);
        }
    }
}

결과

888 


※만약 c#버전에 의한 오류가 발생한다면 아래 이미지 참고





params

배열 형태의 매개변수를 사용지정 할수 있습니다.

일반적인 배열의 형태 뿐만 아니라 여러가지의 매개 변수들을 받을수 있습니다.


using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Plus (params object[] obj)
        {
            for (int i = 0; i < obj.Length; i++)
            {
                Console.Write("obj[{0}] : {1}", i, obj[i]);
                Console.WriteLine();
            }
        }
        static void Main(string[] args)
        {
            object[] objArray = { 777, 888, 'k', "test"};

            Plus(objArray);
        }
    }
}

결과

obj[0] : 777
obj[1] : 888
obj[2] : k
obj[3] : test


다음 강좌에서는 클래스에 대해 알아보도록 하겠습니다.


1. 목표


반복문(while, do while, for, foreach)의 이해 및 사용

제어문(break, continue, goto, return, throw) 이해 및 사용


2. 반복문 종류


프로그램에서 반복문이란 어떠한 조건을 걸어 이를 만족할 때 까지 코드 또는 코드 블록을 계속 반복 수행하도록

하는 문법입니다. c#에서는 for, while, do while, foreach 까지 총 4개의 반복문이 있습니다.


3. 실습


while문

사용법은 간단합니다. 조건이 참일때 코드를 반복 실행합니다. 주의해야 될점은 조건식에 사용되는 

변수나 또는 코드블록 내에서 반복문을 제어해 빠져나올수 있도록 작성해야 된다는 것입니다.

프로그램에서 무한루프 빠져 나올수 없는 경우도 종종 발생해 확실한 제어가 필요합니다.

while(조건식)
{
    //반복할 코드
}

1. while문 조건식에서 제어하기

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            int nNum = 0;

            while(nNum < 5)
            {
                Console.WriteLine("nNum : {0}", nNum++);
            }
        }
    }
}

2. while문 코드 블럭 내에서 제어하기

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            int nNum = 0;

            while(true)
            {
                Console.WriteLine("nNum : {0}", nNum++);

                if (nNum >= 5)
                    break;
            }
        }
    }
}


결과

nNum : 0
nNum : 1
nNum : 2

nNum : 3

nNum : 4


경우에 따라서 제어하는 방법은 달라질수 있습니다.

1번 소스코드의 경우 일반적으로 사용하는 while문 제어 방법입니다. 조건식에 맞춰 반복되다 코드블럭을 빠져나옵니다.

2번 소스코드의 경우 위험한 방법일수도 있지만 경우에 따라 코드블럭 내에서 특정 조건이 되었을때 break문을 사용하여

제어 할수 있는 방법입니다.


※무한루프

 - 반복문이 제어가 되지 않아 무한대로 반복이 이루어지는 현상입니다.

 - 혹여 아래 코드를 디버그 모드로 실행 하신다면 콘솔창의 x 버튼 또는 

    Shift+F5를 눌러 실행중인 프로그램을 강제 종료 할수 있습니다.


using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            int nNum = 0;

            while(true)
            {
                Console.WriteLine("nNum : {0}", nNum++);
            }
        }
    }
}

결과

nNum : 0

nNum : 1

...

nNum : 102

...


while문의 조건식이 언제나 true이기 때문에 항상 코드블럭을 실행 시키고

코드블럭 내에서도 제어를 하는 코드가 없어 nNum의 값만 증가되고 무한루프에 빠지게 됩니다.


do while문

while문과는 다르게 특이한점이 무조건 한번은 먼저 실행되고 반복문의 조건식을 확인합니다.

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            int nNum = 5;

            do
            {
                Console.WriteLine("nNum : {0}", nNum--);
            }
            while (nNum > 0);
        }
    }
}

결과

nNum : 5
nNum : 4
nNum : 3

nNum : 2

nNum : 1




for문

복잡해 보일수 있는 반복문이지만 사용하기 편한 반복문입니다.


for문의 구성

 - 초기화 : 반복문을 시작하기전 처음 한번만 실행되는 코드입니다. 때문에 반복문에서 사용할 변수를

              선언하고 값을 할당 합니다.

 - 조건식 : 조건식이 true일때 반복 실행 합니다.

 - 반복식 : 매번 코드블럭이 실행이 끝나고 마지막에 실행 됩니다.


[초기화]->[조건식] --ture-->[코드 실행] -->[반복식]-->[조건식]--false-->[종료]

for(초기화; 조건식; 반복식;)
{
    //반복할 코드
}

for문에서 초기화를 할때 변수 이름은 많은 프로그래머들이 암묵적으로 i, j, k... 순서로 이름을 사용합니다.

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            for(int i = 0; i < 5; i++)
            {
                Console.WriteLine("Num : {0}", i);
            }
        }
    }
}

결과

Num : 0

Num : 1

Num : 2

Num : 3

Num : 4


※ 다중 for문 - 코드를 작성하다보면 수학적 논리(2차원 배열)를 구현하거나 필요에 의해서 반복문을 겹쳐서 사용

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            for (int i = 1; i < 2; i++)
            {
                for (int j = 1; j < 5; j++)
                {
                    Console.WriteLine("{0} * {1} = {2}", i, j, i * j);
                }
            }
        }
    }
}

결과

1 * 1 = 1

1 * 2 = 2

1 * 3 = 3

1 * 4 = 4


다중 for문을 사용하여 1단~9단까지 구구단을 완성해보세요.



foreach문
foreach문을 사용하기 앞서 배열에 대해 알고 넘어 가도록 하겠습니다.
우리가 특정 타입의 변수를 선언하고 값을 할당 하는 방법은 이제 자주 접하여 알수 있습니다.
배열의 경우는 똑같은 타입의 변수를 순서대로 가지고 있는 데이터 덩어리 입니다.

int[] nArr = {1,3,5,7,9,2,4,6,8};
이렇게 같은 타입에 복수의 값을 선언하게 되면 각각의 변수를 선언 하지않고 대신 nArr[인덱스]의 형태로
값에 접근할수 있는 구조를 가지게 됩니다. 여기서 인덱스는 0부터 시작합니다.


이렇게 규칙적인 데이터 구조를 가진 배열을 이용하여 사용할수 있는 반복문이 foreach문 입니다.

foreach(변수 in 데이터 배열)
{
    //실행할 코드
    //데이터 배열의 각 인덱스를 변수가 참조하여 값을 확인 할수 있습니다.
}


using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] nArr = {1, 3, 5, 7, 9, 2, 4, 6, 8 };

            Console.WriteLine("nArr[{0}] = {1}", 0, nArr[0]);
            Console.WriteLine("nArr[{0}] = {1}", 3, nArr[3]);
            Console.WriteLine();


            int nIndex = 0;

            foreach(int i in nArr)
            {
                Console.WriteLine("nArr[{0}] = {1}", nIndex++, i);
            }
        }
    }
}

결과

nArr[0] = 1
nArr[3] = 7


nArr[0] = 1

nArr[1] = 3

nArr[2] = 5

nArr[3] = 7

nArr[4] = 9

nArr[5] = 2

nArr[6] = 4

nArr[7] = 6

nArr[8] = 8


이렇게 foreach문은 정규화된 데이터 배열들을 반복적으로 처리해야 되는경우 손쉽게 데이터 배열에 접근하여

값을 사용할수 있습니다. 또한 배열 데이터들을 처리할때 for문이나 while문 보다 foreach문이 반복 처리 속도가 더 좋습니다.


4. 제어문 종류


제어문에는 break, continue, goto, return, throw 이렇게 c#에서는 5가지의 제어문을 제공합니다.

우리는 이중에서 break, continue, goto문만 우선적으로 다루어 보도록 하겠습니다.



5. 실습


break문

while문과 switch문에서 사용한것 처럼 해당 코드블럭를 중지하고자 할때 쓰는 문법입니다.

강좌 4와 while문 실습 참조


continue문

continue는 break와 달리 코드블럭을 중지하지 않고 다음 반복을 계속 수행하도록 하는 문법입니다.

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            for (int i = 0; i < 5; i++)
            {
                if (i == 3)
                    continue;

                Console.WriteLine("Num : {0}", i);
            }
        }
    }
}

결과

Num : 0

Num : 1

Num : 2

Num : 4






goto문

goto문은 점프문 이라부르는데 위치와 상관없이 원할때 원하는 위치로 이동하여 코드를 실행하는 무지막지한

녀석입니다. 과도한 goto문을 사용시 가독성도 떨어지고 코드의 흐름을 꼬이게 만드는 주범입니다.

항상 주의하며 사용해야되고 꼭 필요한 상황이 아니면 사용하지 않는 것이 좋습니다.

goto 레이블A;
...
...
레이블A:
    //이어지는 코드

소스코드와 같이 반복문 처럼 사용할수도 있고 필요없는 연산이나 인위적으로 코드를 점프하여 이동  할 수 있습니다.

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            int i = 0;

            AAA:
            Console.WriteLine("AAA : {0}", i);
            i++;

            if (i == 5)
                goto BBB;

            goto AAA;
            BBB:

            Console.WriteLine();
            Console.WriteLine("BBB : {0}", i);
        }
    }
}

결과

AAA : 0
AAA : 1

AAA : 2

AAA : 3

AAA : 4


BBB : 5


다음 강좌에서는 메서드(함수)에 대해 알아보도록 하겠습니다.


수고하셨습니다.




1. 목표


연산자의 종류와 이해

디버그 모드 사용해 보기

분기문의 종류와 이해


2. 연산자의 종류


 종류

연산자 

 사용법

  이항 연산자

 +, -, *, /, % 

 a = b + c

  복합 할당 연산자

 +=, -=, *=, /=, %= a += b

  논리 연산자

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

 a > b

  이항 논리 연산자

 &&, ||, !

 a && b

  비트 이항 연산자

 &, |, ^, <<, >>

 a >> b

  증감 연산자

 ++, --

 ++a, a++

  삼항 연산자

 ? :

 (a > b) ? a : b


프로그래밍에서 많은부분 기본 수학의 계산방식과 동일하고 연산 순서는 왼쪽에서 오른쪽으로 우선 연산 되고

일부 복합 할당 연산(+=, -=...), 전위(++a, --a), 단항 형식은 일부 오른쪽에서 왼쪽으로 연산 됩니다.

자세한 사항은 코드와 함께 알아보도록 하겠습니다.


3. 실습


이항 연산자
using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            //이항 연산자
            //+, -, *, /, % 
            int a = 28;
            int b = 10;
            
            Console.WriteLine("{0} + {1} = {2}", a, b, a + b);
            Console.WriteLine("{0} - {1} = {2}", a, b, a - b);
            Console.WriteLine("{0} * {1} = {2}", a, b, a * b);
            Console.WriteLine("{0} / {1} = {2}", a, b, a / b);
            Console.WriteLine("{0} % {1} = {2}", a, b, a % b);

            //C# 에서는 다른 언어와 다르게 실수 % 연산이 됩니다.
            float f1 = 3.141592f;
            float f2 = 2.141592f;
            float result = f1 % f2;
            Console.WriteLine("{0} % {1} = {2}", f1, f2, result);
        }
    }
}

결과

28 + 10 = 38

28 - 10 = 18
28 * 10 = 280
28 / 10 = 2

28 % 10 = 8

3.141592 % 2.141592 = 1
 


코드내에 주석과 마찬가지로 C#에서는 다른 언어와 다르게 실수 % 연산이 됩니다.

실수의 나머지 연산에서 나머지의 결과 값은 몫이 정수일때의 나머지 값을 의미합니다.


복합 할당 연산자

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            //복합 할당 연산자
            //+=, -=, *=, /=, %=
            int a = 28;
            int b = 10;
            
            Console.WriteLine("{0}", a += b);
            Console.WriteLine("{0}", a -= b);
            Console.WriteLine("{0}", a *= b);
            Console.WriteLine("{0}", a /= b);
            Console.WriteLine("{0}", a %= b);
        }
    }
}

결과

38
28
280

28

8
 


복합 할당 연산자의 경우 값을 할당함에 있어 a 와 b 의 연산이후 다시 a에 값을 재할당 하는것을 의미합니다.

a = a+b 와 같은 결과 값을 가집니다. 재할당의 기준은 왼쪽 피연산자에게 연산이후 재할당 합니다.


값의 변화에 대해 디버깅을 통해 하나씩 확인해 봅시다.

강좌 2장에서 언급한 디버그 모드의 기능을 활용하면 쉽게 값의 변화를 알수 있습니다.

이 기능은 프로그램에 버그가 발생하였을때 자주 사용하는 기능으로 매우 중요합니다.




디버그 모드

 디버깅을 하고 싶은 곳에 클릭

② F9번을 누르면 왼쪽에 붉은색 브레이크 포인트가 생성

③ 디버그 모드인 F5번을 누르면 프로그램 시작과 함께 브레이크 포인트가 있는 곳에서 코드 실행 정지

    값이 보고싶은 변수위에 마우스를 올려 놓으면 값을 확인가능

④ 디버그 모드일때 주변 인터페이스가 변경 되는것을 확인 할수 있는데 하단에 조사식1 탭을 사용하여

    변수의 값을 추적하며 확인할수 있음 ( 변수를 클릭 드래그하여 블럭을 씌운뒤 조사식1 탭으로 옴겨놓음)


※ 코드가 실행 정지 되었을때 다시 F5번을 누르면 다음 브레이크 포인트로 이동이 됩니다.

디버그 모드일때 추가로 확인하고 싶은 곳이 있다면 해당 위치를 클릭후 F9을 눌러 브레이크 포인트를

추가하실수 있습니다. 코드 실행이 정지된 상태에서 F10을 누르면 다음 라인까지 코드실행 F11을 누르면

다음 라인까지 코드가 실행되지만 다른 참조된 라이브러리까지 차례로 들어가서 코드가 실행되는 것을

확인하실수 있습니다.


논리 연산자

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            //논리 연산자
            //>, <, >=, <=, ==, !=
            int a = 1;
            int b = 2;
            int c = 2;

            Console.WriteLine("a > b = {0}", a > b);
            Console.WriteLine("a < b = {0}", a < b);
            Console.WriteLine();

            Console.WriteLine("a >= b = {0}", a >= b);
            Console.WriteLine("a <= b = {0}", a <= b);
            Console.WriteLine("b <= c = {0}", b <= c);
            Console.WriteLine();

            Console.WriteLine("a == b = {0}", a == b);
            Console.WriteLine("b == c = {0}", b == c);
            Console.WriteLine("a != b = {0}", a != b);
            Console.WriteLine("b != c = {0}", b != c);
        }
    }
}

결과


 a > b = False

 a < b = True


 a >= b = False

 a <= b = True

 b <= c = True


 a == b = False

 b == c = True

 a != b = True

 b != c = False



※ 논리 연산 결과가 맞다면 True 틀리다면 False로 리턴됩니다.



이항 논리 연산자


논리곱( && : AND) 연산

두개의 값이 모두 True일 경우에만 True


논리합( || : OR) 연산

두개의 값중 하나라도 True이라면 True


부정( ! : NOT) 연산

True라면 False, False 라면 True


using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            //이항 논리 연산자
            //&&, ||, !
            Console.WriteLine("논리곱(AND) 참 && 참 = {0}", true && true);
            Console.WriteLine("논리곱(AND) 참 && 거짓 = {0}", true && false);
            Console.WriteLine("논리곱(AND) 거짓 && 참 = {0}", false && true);
            Console.WriteLine("논리곱(AND) 거짓 && 거짓 = {0}", false && false);
            Console.WriteLine();

            Console.WriteLine("논리합(OR) 참 || 참 = {0}", true || true);
            Console.WriteLine("논리합(OR) 참 || 거짓 = {0}", true || false);
            Console.WriteLine("논리합(OR) 거짓 || 참 = {0}", false || true);
            Console.WriteLine("논리합(OR) 거짓 || 거짓 = {0}", false || false);
            Console.WriteLine();

            Console.WriteLine("부정(!) !참 = {0}", !true);
            Console.WriteLine("부정(!) !거짓 = {0}", !false);
        }
    }
}

결과

 논리곱(AND) 참 && 참 = True

 논리곱(AND) 참 && 거짓 = False

 논리곱(AND) 거짓 && 참 = False

 논리곱(AND) 거짓 && 거짓 = False


 논리합(OR) 참 && 참 = True

 논리합(OR) 참 && 거짓 = True

 논리합(OR) 거짓 && 참 = True

 논리합(OR) 거짓 && 거짓 = False


 부정(!) !참 = False

 부정(!) !거짓 = True




비트 이항 연산자

비트 연산자를 알기에 앞서 컴퓨터의 최소 데이터 단위는 Bit로 0과 1을 사용하는 2진수을 사용합니다.

1byte의 데이터는 0000 0000과 같습니다.


using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            //비트 이항 연산자
            //&, |, ^, <<, >>
            byte a = 15;
            byte b = 5;
            string s = "";

            s = Convert.ToString(a, 2).PadLeft(8, '0');      //8자리의 비트로 표현하기 위해 사용한 코드
            Console.WriteLine("a     : {0}, Bit : {1} ", a, s);
            s = Convert.ToString(b, 2).PadLeft(8, '0');
            Console.WriteLine("b     : {0} , Bit : {1} ", b, s);
            Console.WriteLine();

            s = Convert.ToString(a&b, 2).PadLeft(8, '0');
            Console.WriteLine("a & b : {0} , Bit : {1} ", a & b, s);
            Console.WriteLine();

            s = Convert.ToString(a | b, 2).PadLeft(8, '0');
            Console.WriteLine("a | b : {0}, Bit : {1} ", a | b, s);
            Console.WriteLine();

            s = Convert.ToString(a ^ b, 2).PadLeft(8, '0');
            Console.WriteLine("a ^ b : {0}, Bit : {1} ", a ^ b, s);
            Console.WriteLine();

            s = Convert.ToString(a << 1, 2).PadLeft(8, '0');
            Console.WriteLine("a << 1 : {0}, Bit : {1}", a << 1, s);

            s = Convert.ToString(a << 2, 2).PadLeft(8, '0');
            Console.WriteLine("a << 2 : {0}, Bit : {1}", a << 2, s);
            Console.WriteLine();

            s = Convert.ToString(a >> 1, 2).PadLeft(8, '0');
            Console.WriteLine("a >> 1 : {0}, Bit : {1}", a >> 1, s);

            s = Convert.ToString(a >> 2, 2).PadLeft(8, '0');
            Console.WriteLine("a >> 2 : {0}, Bit : {1}", a >> 2, s);
        }
    }
}

결과

a        : 15,  Bit : 00001111

b        : 5 ,  Bit : 00000101


a & b  : 5 ,  Bit : 00000101


a  |  b : 15,  Bit : 00001111


a ^ b : 10,   Bit : 00001010


a << 1 : 30, Bit : 00011110

a << 2 : 60, Bit : 00111100


a >> 1 : 7, Bit : 00000111

a >> 2 : 3, Bit : 00000011


비트연산은 최고의 연산속도를 자랑합니다. 프로그램 최적화나 최소한의 데이터를 이용하여 연산을 하기위해 사용 합니다.

컴퓨터 입장에서는 다른 추상화된 정보와 달리 별도의 과정없이 의미가 해석되어 전달 되기때문에 빠를수 밖에 없습니다.


a(15) : 0000 1111,  b(5)  : 0000 0101


&(AND) : 양쪽의 비트가 1일때 1

 0000 1111

 0000 0101

 ---AND---

 0000 0101

 | (OR)   : 양쪽의 비트중 하나라도 1이면 1

 0000 1111

 0000 0101

 ----OR----

 0000 1111 

^ (XOR) : 양쪽의 비트가 달라야 1 

 0000 1111

 0000 0101

 ---XOR---

 0000 1111 

 << : 비트를 왼쪽으로 이동
이동한 비트는 0으로 채워짐

 0000 1111

 ----<<----

 0001 1110 

 >> : 비트를 오른쪽으로 이동

데이터 값을 넘어가는 비트는 삭제됨

 0000 1111

 ---->>----

 0000 0111 1(삭제)




증감 연산자

전위, 후위로 나뉘는데 기본적으로 ++는 값에 1을 증가 시키거나 1을 감소 시키는 연산자 입니다.

전위의 경우 값을 먼저 증가 시키고 코드가 실행되며 후위의 경우 코드가 실행되고 값이 증가 됩니다.

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            //증감 연산자
            //++, --

            int a = 1;

            Console.WriteLine("현재 값      : {0}",   a);
            Console.WriteLine();

            Console.WriteLine("전위 증가 값 : {0}", ++a);
            Console.WriteLine("후위 증가 값 : {0}", a++);
            Console.WriteLine("현재 값      : {0}", a);
            Console.WriteLine();

            Console.WriteLine("전위 감소 값 : {0}", --a);
            Console.WriteLine("후위 감소 값 : {0}", a--);
            Console.WriteLine("현재 값      : {0}", a);
        }
    }
}

결과

현재 값       : 1


전위 증가 값 : 2 

후위 증가 값 : 2

현재 값        : 3


전위 감소 값 : 2

후위 감소 값 : 2

현재 값       : 1



삼항 연산자

코드를 짜다보면 가독성이라는 말을 많이 하게 됩니다. 코드가 한눈에 들어오고 읽기 편한 코드 형태를

가독성이 좋다라고 말합니다. 삼항 연산자는 가독성을 올리고 간단한 논리의 오류가 적어보일때 사용하면 좋습니다.


(논리) ? 논리가 참 일때 참조되는 값 : 논리가 거짓 일때 참조되는 값

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            //삼항 연산자
            //? :
            int num1 = 10;
            int num2 = 20;
            int numTrue = 77;
            int numFalse = 88;
            
            int r = (num1 > num2) ? numTrue : numFalse;
            Console.WriteLine(r);
        }
    }
}

결과

88 



4. 분기문의 종류


 종류

짝을 이루는 문법

 

  if문

 if, else if, else

 a = b + c

  switch문

 switch, case, break, default

 a += b
if 문

if문과 짝이되는 문법 : if, else if, else

if (조건식 1) // 예) a > b
            {
                //조건식 1이 참인 경우 중괄호 안의 코드 실행
            }
            else if (조건식 2) //예) a < b
            {
                //조건식 1이 거짓이고 조건식 2가 참인 경우 중괄호 안의 코드 실행
            }
            else
            {
                //모든 조건식이 거짓인 경우 중괄호 안의 코드 실행
            }

if문은 다양하게 사용가능 합니다.

-if 단독으로 사용

-if, else if 사용 가능

-if, else 사용 가능

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            int a = 10;
            int b = 20;

            if(a > b)
                Console.WriteLine("a는 b보다 크다");

            if(a > b)
                Console.WriteLine("a는 b보다 크다");
            else
                Console.WriteLine("a는 b보다 크지 않다");


            if(a == b)
            {
                Console.WriteLine("a와 b는 같다");
            }
            else if(a < b)
            {
                Console.WriteLine("b는 a보다 크다");
            }
            else
            {
                Console.WriteLine("a는 b보다 크다");
            }
        }
    }
}

결과

a는 b보다 크지 않다
b는 a보다 크다


if, else if, else의 중괄호 {, }는 제외가 가능하지만 한줄 이상의 코드에서는 오히려 가독성이 떨어지고 버그를 유발할수 있습니다.




switch 문

switch 문과 짝이 되는 문법 : switch, case, break, default

switch (조건식1)
{
    case 조건식: //해당 조건식1 형식( 정수, 문자, 문자열 )에따라 조건식을 써야 합니다.
        {
            //조건식과 같다면 중괄호 안의 코드 실행
            //break를 만나면 switch문을 빠져나감
            break;
        }
    case 조건식: //순차 적으로 case 단위로 비교되고 조건식과 같다면 중괄호 안의 코드 실행
        {
            break;
        }
    default: //모든 case를 비교하였지만 같은 조건식이 없다면 중괄호 안의 코드 실행
        {
            break;
        }
}

switch문은 조건식1에 따라 위에서 아래로 순차적으로 case와 비교 진행되며 default문은 제외 가능합니다.


using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            int a = 13;
            switch (a)
            {
                case 10:
                    {
                        Console.WriteLine("a 는 10 이다");
                        break;
                    }
                case 13:
                    {
                        Console.WriteLine("a 는 13 이다");
                        break;
                    }
            }
            Console.WriteLine();


            string str = "파랑";
            switch (str)
            {
                case "빨강":
                    Console.WriteLine("좋아 하는 색은? {0}", str);
                    break;
                case "노랑":
                    Console.WriteLine("좋아 하는 색은? {0}", str);
                    break;
                case "파랑":
                    Console.WriteLine("좋아 하는 색은? {0}", str);
                    break;
                default:
                    Console.WriteLine("좋아 하는 색이 없다");
                    break;
            }
        }
    }
}

결과

a 는 13이다

좋아 하는 색은? 파랑


case의 중괄호 {, }는 제외 가능하나 코드가 늘어나면 가독성이 떨어지고 실수를 할수 있습니다.

코드는 명확한것이 가장 좋고 여러사람이 한 프로젝트를 공유하여 개발할때 가독성을 떨어트리거나 자신만 알아볼수 있는 코드는

삼가 해야됩니다.


다음장에서는 반복문과 제어문에 대해 알아보도록 하겠습니다.

수고하셨습니다.


+ Recent posts