본문 바로가기
IDE & Tool/Visual Studio

[C#] 공부일지

by 성은2 2021. 9. 23.

Chapter1 Class

고지식한 객체지향언어 C#
// 모든길은 클래스로 통한다.

class Monster {

}

 

 

 

Chapter2 NameSpace

// 네임스페이스는 개념의 분류, 클래스들을 만들때 중복문제 등을 피하기 위해 namespace를 지정해서 분류

// A, B 2명의 프로그래머가 있다.

// 두 프로그래머 모두 Potion이라는 동일한 이름의 클래스를 선언했지만, NameSpace의 구분으로 인해 중복오류가 나지않는다. 

namespace Aworld { 
// A 프로그래머
class Potion
    {

    }
}

// B 프로그래머 
class Potion
{

}

namespace _02NameSpace
{
    class Program
    {
        static void Main(string[] args)
        {
        }
    }
}

 

Chapter3 코드 읽는법, 디버깅 하는법 (Visual Studio -> F10)

 

// 코드를 짤 때 고민을 하고, 문제 해결을 하는것은 디버깅으로 눈으로 보면서 하자.

namespace codeReadingStudy
{
    class Program
    {
        // Main을 만나는순간, OS가 코드를 읽기 시작하고, 실행이 시작된다.
        static void Main(string[] args)
        {
            // 항상 디버깅이 중요하다.
            // 비쥬얼 스튜디오 에서는 F10을 이용하여 한줄 한줄 디버깅 가능
            Console.WriteLine("하하");
            Console.WriteLine("하하");
            Console.WriteLine("히히");
        }
    }
}




 

Chapter4 멤버변수 - 클래스 내부에 있어서 멤버변수

namespace _04MemberVar
{
    class Program
    {   
            // 선언을 함과 동시에 초기화하는 것 : 리터럴 초기화

            // 코드를 실행한다는 것은 하드디스크에 있던 exe가 램에 올라가게 (복사) 되는것
            // 무조건 공간을 차지한다. (메모리를 쓰는것)
            
            // 즉 변수의 선언은 

            // 램에 30번지(주소값)에 4바이트만큼의(int형은 4바이트) 공간을 만들어(메모리를 차지한다) 400 값을 넣는것.

            int Att = 400;
        
    }
}

 

Chapter5 멤버함수

// 어떤 목적으로 만들고, 어떤 속성이 필요한지를 생각하며 만들고 쓰는것에 익숙해져야 한다.

// 주인공이 있다. Player라는 클래스를 만들었다.
class Player
{
    // 주인공은 공격력이 있다. 멤버변수를 만들었다. 
    int ATT;
    int HP;
    // 이런 명사만으로는 부족해
    // 행동이 필요하다. 함수를 만들자. 
    
    // 주인공은 HP로 움직일 수 있다. Move멤버함수를 만들었다.
    void Move()
    {
         // 어떤 내용이 들어갈지 보다 어떤 쓰임으로 만들지가 우선
    }


    // 주인공은 ATT로 공격할 수 있다. Attack 멤버함수를 만들었다.
    void Attack()
    {

    }

}

 

Chapter6 지역변수

class Player
{
    // 멤버변수 - 클래스 내부에 있어서 멤버변수
    int Att = 0;
    int Hp;

    void Fight()
    {
        // 선언되는 순간 지역변수는 메모리화
        int Damage = 0;

        Console.WriteLine("플레이어가 싸운다.");
    }
}

namespace _06LocalVar
{
    // c#은 프로그램의 시작조차도 클래스 안에 묶어놔야한다.. 고지식한 언어다..
    class Program
    {
        // 시작용 함수
        static void Main(string[] args)
        {
            // 함수 안에 있는 녀석 : 지역변수
            // 지역변수 규칙 : 내부에서만 사용가능
            // 실행하고자 하는 내용
            // 객체화라고 하는 중요한 작업
            // Player의 설계대로 플레이어를 만드는데, 그 이름을 NewPlayer1 라고 해라.
           /* Player NewPlayer1 = new Player();
            Player NewPlayer2 = new Player();*/
        }
    }
}

 

Chapter7 클래스 접근 

class Player
{   // ->  Player라는 이름의 클래스 내부

    // 객체지향의 캡슐화, 은닉화
    // 접근제한 지정자.
    // 클래스 안에 모든걸 공개하지 않고, 필요한것만 공개.

    public int HP;  // 외부에도 공개
    protected int ATT;  // 자식에게만 공개
    private int DEF;  // 내부에만 공개, 접근제한 지정자 생략시 기본값은 private
    
    public void Fight()
    {
        Console.WriteLine("싸운다.");
    }
    
}

// -> 여기서부턴 클래스의 외부
namespace _07ClassAccess
{
    class Program
    {
        static void Main(string[] args)
        {
            Player NewPlayer = new Player(); 

            // 선언은 이렇게
            // 위에서 만들어진 Player 객체의 내용을 사용하기 위해서는
            // 객체의 이름 . 을 사용해서 접근
            // 접근제한지정자가 public이 아닌경우, 외부인 요기에서는 사용못한다.
            
            // 접근해서 사용은 이렇게.
            NewPlayer.HP = 0;
            NewPlayer.Fight();
        }
    }
}

 

Chapter8 함수의 선언, 함수의 사용, 함수를 사용하는 이유

namespace _08FuncEx
{
    class Player
    {
        // 접근제한지정자 쓰지 않으면 default 는 private
        // 일반적으로 외부에서 멤버변수에 접근하지 못하는게 좋다.
        // 접근이 가능하면 어떤 오류가 발생할지..
        int AT;
        int HP;
        private int LV = 1;

        // 플레이어 레벨이 얼마인지 알고 싶다.
        // 인자값이 아니라
        // return 값을 사용해보자.
        // 리턴값이란 객체가 자신의 상태를 외부에 알려주는 용도
        // 외부에 알려줘야 하기 때문에 알리는 순간 함수는 끝.
        // 즉, return 을 만나면 함수종료 
        public int GetLv()
        {
            return LV;
        } 
        public  void SetHP(int _HP)
        {
            HP = _HP;
        }

        // 함수는 보통 선언과 , 내용으로 나뉨

        // void(리턴값) Func[이름 혹은 식별자]()[인자값]
        // 인자값에는 외부에서 한개의 int 값을 _Dmg라는 이름으로 넣어주겠다. 
        public void Damage1(int _Dmg)
        {
            HP = HP - _Dmg;
        }

        // 인자값의 개수는 상관없다.
        public void Damage2(int _Dmg, int _SubDmg)
        {
            HP = HP - _Dmg;
            HP = HP - _SubDmg;
        }

        

        // public int 로 바꾸어야 오류가 안남. int 형이기 때문

        public void DamageTOHPReturn(int _Dmg)
        {
            HP = HP - _Dmg;
            // 리턴값은 자신이 리턴해주려는 자료형과
            // 동일한 자료형이어야 한다.
            return HP;
        }

        // 함수란 보통 클래스 외부와의 소통을위해 만든다.
    }


    class Program
    {
        static void Main(string[] args)
        {
            Player NewPlayer = new Player();
            // NewPlayer.HP = 0;

            /* 멤버 변수에 직접 접근가능하게 했다가 내가 만들 플레이어가 죽어버릴 수 있음 ㅇ.ㅇ

                (어떤 오류가 발생할지 모른다는것..) */

            // 함수를 사용해서 세팅할경우 
            // 디버깅이 가능해지고
            // HP가 100이되는순간, 10이 되는순간 등.. 체크가 가능해진다.
            // 그래서 멤버변수 자체를 접근해서 건드는게 아니라, 함수를 사용하는것이다.

            // 외부의 값을 받아서 객체가 내부의 상태를 변화시키기위해 함수를 선언하는 경우가 많다.
            NewPlayer.SetHP(200);
            NewPlayer.Damage1(10);
            NewPlayer.Damage2(10, 20);
            NewPlayer.GetLv();
        }
    }
}

 

Chapter9 연산자

namespace _09Operator
{
    class Program
    {
        static void Main(string[] args)
        {
            // = 대입 연산자. 변수에 값을 대입
            int Result = 0;
            int Left = 3;
            int Right = 7;

            // 산술연산자, 주의할점 : 나누기와 나머지는 0을 넣으면 안됨 -> 제로디비전
            // + - * / %

            // bool 논리 연산자
            // 참과 거짓을 연산한다.
            bool BResult = true;

            BResult = !true; // 논리연산의 반대를 반환한다. true면 false를, false면 true
            BResult = true && false;   // AND : 둘 다 true일때만 true
            BResult = true || false;   // OR : 둘 중 하나만 true여도 true
            BResult = true ^ false;    // XOR : 양쪽이 다르면 true, 같으면 false        

            // 축약 연산자
            Result = 0;
            // 아래 두 코드는 동일하다.
            // 산술 연산자면(/= , *=) 모두 축약 가능 
            Result = Result + 10;
            Result += 10;
            
        }
    }
}

 

Chapter10 Memory(Func)

// 메모리는 모든 프로그램의 근간이 되는 개념 (C#만을 위한 공부가 아님)
// 모든 프로그램은 메모리를 사용한다.
class Player
{

}

class Program
{
    static void Main(string[] args)
    {
        // 객체를 만들었다 -> 메모리를 지불(사용)했다.
        Player Player01 = new Player();    

        // 메모리 크기는 선언된 지역변수 + a로 계산되는데
        // 최소한 지역변수를 다 포함할 수 있는 크기만큼 메모리화 되어서 스택에 쌓인다.

        // 함수의 인자값은 지역변수
        // 지역변수의 특징 : 함수가 끝나면 사라진다.
    }
}

 

Chapter11 Memory(value)

class Player
{
    int AT;
    int HP;

    public int Test (int _Dmg)
    {
        // 이 안에 지역변수에서 벌어지는 일은
        // 안에서의 일뿐. 메모리에 변화가 없다.
        _Dmg = 1000;

        // return _Dmg;
    }
}

namespace _11Memory00_Value_
{
    class Program
    {
        static void Main(string[] args)
        {
            Player NewPlayer = new Player();

            // 함수의 인자값
            int value = 0;
            NewPlayer.Test(value);

            // 함수가 실행된 후 value는 몇이 됐을까? 0일까 1000 일까!
            // 0이 나온다.
            Console.WriteLine(value);

            // main의 위치 : 98(임시로 이쯤 있다고 생각)
            // main의 위치에 있는 value의 값 : 0

            // Test의 위치 : 74
            // Test의 위치에 있는 _Dmg의 값 : 0(★98바이트에 있는 100을 받는다.)

            // 해당 함수 내에서 값이 바뀌어도 value와는 아무  상관없다~ (별개의 지역변수)
            // 함수 내에서 지역변수를 아무리지지고 볶아도 외부로 보내주지 않으면 안된다...

            // Test함수가 끝나면 지역변수는 메모리에서 지워진다.
            // 즉 value의 값은 변하지않으니까 0이다.

            // return을 사용한다면! 그제서야 value가 1000으로 변할 수 있다.
            value = NewPlayer.Test(value);  // 값형의 처리

            /* 다시정리 */
            // 변수는 메모리 구조에서 스택(Stack)에 할당(Push)된다.
            // 스택에는 지역변수, 함수의 복귀주소, 매개변수 등이 저장되고, 생성된 블록 내에서만 유효하다.
            // 즉, 함수가 종료되거나 블럭이 닫히면 스택에서 제거(pop)된다.
        }
    }
}

 

Chapter12 Memory 메모리 구조 / call-by-value / call-by-reference

// c#의 메모리 구조는 c, c++과는 달리 CLR을 기반으로 처리한다.
// 즉, c#으로 만든 프로그램들은 .net에 의해 작동 되기 때문에 clr이 관리하는 메모리 공간을 사용한다. 
// 이 메모내용은 C#의 메모리 구조 설명보다는 
// 프로세스의 메모리구조와 call-by-value, call-by-reference의 설명이라 보면된다.

// call by Value(값 전달) - 값에 의한 호출, 함수에 매개 변수의 내용물을 전달 하여 복사해서 사용 하는 방식이다.
// call by Address, Call by Reference (주소 전달) 

class Player
{
    public int AT = 10;
    public int HP = 100;

    // 함수에 매개변수로 class를 넣을 수 있다.
    // 클래스가 객체화 되었다. = 레퍼런스형
    public void ATT(Monster _Monster)
    {
        _Monster.HP -= AT;

        // 레퍼런스는 에 생선된 메모리의 위치를 가리킨다.

        // 즉, 레퍼런스 값은 어느 위치를 가리키기 때문에, 그 위치(힙에 생성된)에서 값이 변경되면
        // 90으로 변경된 채로 출력되는것이다.

        // Player NewPlayer -> 스택영역에 생성
        // new Player() -> 힙 영역에 생성
    }
}

class Monster
{
    public int AT = 10;
    public int HP = 100;

    public void ATT(Player _Player)
    {
        _Player.HP -= AT;
        // 플레이어의 HP를 몬스터의 공격력만큼 깎음(-)
        // 플레이어의 HP = 90이됨.
    }
}


namespace _12Memory02_Reference_
{
    class Program
    {
        static void Main(string[] args)
        {
            Player NewPlayer = new Player();
            Monster NewMonster = new Monster();

            // * 레퍼런스형
            NewMonster.ATT(NewPlayer); 

            // 몬스터가 공격을 했다. Player의 HP가 90으로 변경되었다! -> 레퍼런스형이어서 가능한일
            NewPlayer.ATT(NewMonster); 

            // 플레이어가 공격을 했다. Monster의 HP가 90으로 변경되었다! -> 레퍼런스형이어서 가능한일

            // 전 강의에서 배운대로라면, return 하지않으면 안바뀌어야 하는것 아닌가?
            // 오해의 소지가 있다. 바뀌는게 정상이다.

            // 클래스가 객체화된 녀석 (ex_ NewMonster.ATT(NewPlayer))은 레퍼런스 형이라고 하는 자료형이 되고
            // 지금까지 사용한 int, bool 이런 녀석들은 값형 이라고 한다.

            // 값형과 레퍼런스형은
            // 메모리의 위치와 사용법이 다르기 때문에 다르게 동작한다.

            // 값형과 레퍼런스형을 구분해야 한다.
            // 값형 : 기본자료형을 선언만 한것 
            // 레퍼런스형 : 클래스를 nwe 클래스명(클래스명); 해서 만든 클래스 객체

            /* 힙이란 ?
             * 메모리 구조에는 힙(Heap)이 있는데, 실행시간(run time)중에 동적으로 할당된 변수들이 저장되는 공간.

             * new 클래스명(); 해서 만들어진 클래스 객체들의 본체가 위치한 곳
             * 프로그래머가 직접 new와 delete 연산자를 사용해서 메모리의 할당과 해제를 관여하는 곳이지요.
            */ 

            // 스택이란?
            // 함수의 실행 메모리 영역, (그 박스 안에 들어있는 지역변수)이 저장됐다가 종료되며 사라지는 공간
        }
    }
}

 

Chapter13 정적 멤버변수 static 

class Player
{
    // static : 정적 멤버변수
    // - 일반적인 멤버변수와 다른점
    // 정적 멤버변수는 객체화를 하지 않고도 사용가능
    // 사용법이 클래스의 이름만으로 사용 가능
    // 사용법 : 클래스.정적멤버변수명


    public static int PlayerCount = 0; //정적 멤버변수의 선언

    public int AT = 10;  // 일반 멤버변수
    public int HP = 100;  // 일반 멤버변수

    public void Setting(int _AT, int _HP)
    {
        AT = _AT;
        HP = _HP;
    }

}


// 몬스터 100마리가 죽으면 이벤트 발생하고 싶다

class Monster
{
    // int MonsterDeathCount; // 공통으로 쓰일 통계값이니까 3번이나 생성할 필요 없다.
    // 수정
    static int MonsterDeathCount; // 그래서 static으로 선언했다.
    public void Death()
    {
        MonsterDeathCount += 1;
    }
}

class SidePlayer
{
    // 정적 멤버변수는 객체 귀속이 아니라, class에 귀속되고, 데이터 영역에 메모리가 올라간다.
    static public int PlayerCount;

    // static이 붙지 않은 멤버변수들은 멤버변수라고 불리며
    // 객체 하나하나가 따로 사용한다.
    public int x = 0;
}

namespace _13StaticVar
{
    class Program
    {
        static void Main(string[] args)
        {
            Player newPlayer1 = new Player();
            Player.PlayerCount += 1; // newplayer 를 생성할때마다 PlayerCount를 증가시키고 싶어!
            Player newPlayer2 = new Player();
            Player.PlayerCount += 1; // newplayer 를 생성할때마다 PlayerCount를 증가시키고 싶어!
            Player newPlayer3 = new Player();
            Player.PlayerCount += 1; // newplayer 를 생성할때마다 PlayerCount를 증가시키고 싶어!
            // static인 PlayerCount는 객체화 하지 않고도 클래스명으로 접근(.) 하여 사용 하는걸 볼 수있다.
            // 정적 멤버변수는 데이터 영역에 들어가기 때문이다.

            // 정적 멤버 변수는 클래스가 공유하고 싶은 값 
            // 예를 들어 Monster 클래스안에 모든 몬스터의 수를 공유하고 싶다면? 그것을 static 선언하여 사용하자. 
            // 즉 하나의 클래스 내에 모든 객체가 공유하는 변수 = static, 정적 멤버변수

 

             PlayerCount 변수를 static으로 선언하지 않으면 어떻게 될까?

           위의 예제로 보자면, 클래스 내에 선언된 객체(newPlayer1, newPlayer2, newPlayer3)마다 

            PlayerCount라는 변수를 따로 사용하게 되고, 처음 원하던 통계값인 3이 아니라

           각각 PlayerCount = 1  꼴로 데이터가 담기게 된다.

                              


            newPlayer1.Setting(10, 100);
            newPlayer2.Setting(20, 50);
            newPlayer3.Setting(100, 500);

            Monster NewMonster1 = new Monster();
            Monster NewMonster2 = new Monster();
            Monster NewMonster3 = new Monster();

            NewMonster1.Death(); // 1
            NewMonster2.Death(); // 1
            NewMonster3.Death(); // 1
            // 몬스터 1, 몬스터2, 몬스터3 세마리 죽었다. but MonsterDeathCount는 모두 1.
            // static으로 선언하면 의도한대로 MonsterDeathCount는 3
            // ★ static : 모든 객체가 값을 공유할 필요가 있는 데이터를 정의하는데 사용한다.
        }
    }
}