ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Section 1 ~ Section: 7
    👩🏻‍💻 c# 2023. 8. 1. 07:34

    Section 1: data 

    • 4 main types: int, float, string, bool
      • 변수: 선언시 RAM 에 자리 할당
      • 선언, 할당, read
    • Int
      • int: 32 bit / 4 byte 크기 (short: 2 byte)
      • 최상의 비트로 부호표현 
    • Float: 숫자에 f를 붙어야 함.
      • 근사치를 표현 (그렇기에 정수 사용할 수 있는 경우에는 int를 사용하도록)
      • double (8 byte, 더 정밀 표현, f 붙이지 않는다)
    • String 
      • "" 큰 따옴표 붙여 사용. 여러개의 문자열
      • char : 작은 따옴표 ('') 사용. 하나의 문자
      • 정수로 치환되어 저장된다 (아스키코드)
    • Bool : true / false

     

    • 캐스팅: 형식을 변환
      • 예시: 
    int a = 100;
    short b = (short)a;
    • 크기가 큰 데이터를 작은 곳에 넣을 때는 에러메세지: 꼭 명시적으로 표현 해야한다

     

    • 스트링 포맷
    int a = 100;
    string b = (string) a; //불가. string은 클래스여서. 이전과는 다른 경우이다
    int c = 80;
    
    
    //string -> int
    int number = int.Parse(input)
    
    //int -> string
    //1st method
    string.Format("Your number is {0}", a);
    string.Format("Your number is {0}{1}", a, c); 
    
    //2nd method
    string message = $"Your number is {a}"; // 요 방법 추천. 순서 명시 필요 없음

     

    • 산술연산: +, -, *, /, % (홀짝 구분시)
    • 증감연산: a++, a+= 5, ++a;
    • 비교연산: < <= > >= == !=
    • 논리연산: && || !  / 예시: bool result = (isTall && isSmart);

     

    • var 
      • 만능형. 자동추론. 하지만 직접 명시가 직관적이어서 더 좋음.

     

     

    Section 2: 코드 흐름의 제어

    • if / else 문
    • switch
      • switch(choice) -> 에서 choice 는 꼭 정수나 문자열이어야 한다
      • case a:  에서 a는 변수 일수 없다. (변수 일시 앞에 const 붙여서 상수화 해야함)
    • 삼항 연산자:
      • 취향 차이. 가독성 문제
    int number = 24;
    bool isPair;
    //isPair = ( (조건) ? 맞을때; 틀릴때);
    isPair = ( (number % 2) == 0 ? true; false);
    • 상수와 열거형
      • 숫자 하드코딩 하지 말자 -> 가독성 떨어짐
      • 열거형
    int SCISSORS = 0; //상수는 대문자로 
    int ROCK = 1;
    int PAPER = 2;
    
    //보다는 열거형을 쓰자. 가독성 올라간다
    enum Choice
    {
    	Rock,
        Paper,
        Scissors
    }
    //숫자를 지정하지않으면 순서대로 0... assign 된다
    
    enum Choice2
    {
    	Rock = 2,
        Paper = 1,
        Scissors = 3
    }
    //요래 명시 할수도 있음
    
    switch(choice)
    {
    	case (int)Choice.Scissors: //요렇게 쓰면된다
        ...
    }
    • while, for 문
    • break와 continue 로 코드의 흐름을 제어. 
      • continue: 루프가 끝난걸로 가정하고 그 다음 iteration으로 넘어간다. 

     

    함수 (method)

    • 정의방법
    한정자 반환형식 이름(매겨변수 목록)
    {
    
    }
    
    static void HelloWorld()
    {
    	Console.WriteLine("hello");
    }

     

     

    ⭐️ 참조 (진짜) / 복사 (짝퉁)

    static void AddOne(ref int number) //ref : 참조 값을 받겠다고 명시적
    {
    	number += 1;
    }
    
    int a = 0;
    
    Program.AddOne(ref a); //복사가 아니라 참조 값을 보낸다. 이때도 ref 키워드를 붙여서 -> 즉 실제 a 의 값이 변경괴게 된다.
    •  ref, out 
      • 자주 사용하지는 않는다
      • ref 안 쓴 버전이 더 좋다 (반환을 받아서 이 값을 저장하는 방식으로)
      • 물론 ref를 썼을 때 더 편한 경우도 있다. 예를 들면 두 숫자를 서로 스왑하는 경우
      • out은 함수에서 여러 값을 반환하고 싶을 때 쓴다 
    static void Divide(int a, int b, out int res1, out int res2) //out 붙이기
    {
    	res1 = a/b;
        res2 = a%b;
    }
    
    static void Main(string[] args)
    {
    	int num1 = 10;
        int num2 = 3;
        int res1;
        int res2;
        
        Divide(10, 3, out res1, out res2); ////out 붙이기
    }
    
    //out은 여러 개를 반환 하고싶을 때 쓴다 요렇게

     

    오버로딩 (과적) // 이름의 재사용

    • 매개변수 타입이나 갯수가 달라야 한다
    static int Add1(int a, int b){ }
    static int Add1(int a, int b, int c){ }
    
    
    //팁. 
    static int Add(int a, int b, int c = 0){ } // 마지막 매개 변수: 옵션 -> 오버로딩없이 이 함수 여러 상황에서 쓸수 있게 된다
    Add(1,2); 
    Add(1,2,3);

     

     

    Section 3:  TextRPG

    • 구조체 (복사 o, 참조 x)
    struct Player
    {
    	int hp;
        int attack;
    }

     

    Section 3:  객체 지향

    • 함수 기반
      • 절차 지향. 순서 중요 하다.
      • 유지보수 어려움
      • 직관적 장점
    • 객체 지향 (OOP)
      • 클래스: 속성, 필드 & 기능
      • 특징: 은닉성, 상속성, 다형성 

     

    클래스

    • 동적 할당
    class Knight //Knight 라는 틀을 만드는 것
    {
    	public int hp;
        public int attack;
        
        public void move() { }
    }
    
    Knight knight = new Knight();
    //동적 할당 (Heap)
    
    knight.hp = 100;
    knight.Move();
    • 복사와 참조
      • struct는 복사. 클래스는 참조!
      • 깊은 복사 (deep copy) 
    public Knight Clone()
    {
    	Knight knight = new Knight();
        knight.hp = hp;
        knight.attack = attack;
        return knight;
    }// 요런식으로 참조가 아니라 복사를 할 수도 있다

     

    생성자

    class Knight
    {
    	//skip
        
        public Knight() //한정자 + 클래스 이름 + () 
        {
        	hp = 100;
            attack = 10;
        }
    	
        //하나의 생성자 이상을 가질수 있다. 
        public Knight(int hp)
        {
        	this.hp = hp;
            attack = 10;
    	}
    }
    • 클래스의 속성이 너무 많으면 일일이 생성자로 assign 하기 불편. 그래서 생성자에: this() ⭐️
    class Knight()
    {
    	public Knight(int hp): this() //빈 생성자. 즉 Knight() 먼저 호출
        {
        	this.hp = hp;
            attack = 10;
        }
    }

     

    ⭐️⭐️⭐️Static 의 정체

    • 클래스에서 오로지 한개만 존재
    • 즉 인스턴트들이 이 한개의 값을 공유한다
    • 클래스에 종속적이기 때문에 유일성을 보장한다 
    • 클래스 함수들도 static 선언 가능하다
      • static으로 선언된 함수는 그 클래스의 필드에 접근 불가능하다
      • 오로지 static 필드만 접근 가능하다
      • 호출할때는 클래스이름.method이름(); 으로 (보통은 인스턴스이름.method()이다)
        • 예시: Console.WriteLine() 도 클래스이름.method()로 한거

     

    상속성

    • 부모-자식 / 계층관계 정리
    class Player //부모, 기반 클래스
    {
    
    }
    
    class Mage: Player //자식, 파생 클래스
    {
    
    }
    • 여기서 Mage 인스턴스를 만들면 부모 생성자 먼저 호출 -> 후 자식 생성자 호출 된다
      • 이때 부모의 기본 생성자가 아닌 다른 생성자를 호출하고 싶다면 base() 를 활용하여 명시한다.
    class Mage: Player
    {
    	public Mage() : base(10) //부모의 생성자중 인자를 1개 받는 생성자 호출
        {
        	base.hp = 100; //부모의 필드에 접근할때도 base를 써서
        }
    }
    •  그렇다면 함수는?
      • 부모의 method도 상속되고, 자식은 마치 자기 method 인 것 처럼 쓰면 된다. (static 함수는 안되겠지)

     

    은닉성 (보안레벨)

    • 접근 한정자
      • public: 아무나 접근
      • protected: 외부 접근불가 but 자식 클래스는 접근 가능
      • private: 클래스 내부에서만 접근 가능. 자식 클래스도 노노. 
      • 지정 하지않으면 자동적으로 private 으로 간주 된다 
    • 쓰는 이유: 여러사람이 같은 게임 개발 하게되서 파일 복잡해져서 보안
    class Knight
    {
    	private int hp;
        public void SetHp(int hp)
        {
        	this.hp = hp;
        }
    }
    
    Knight knight = new Knight();
    // knight.hp = 3 -> 자 요렇게 하지말고 따로 클래스에서 SetHp 같은 함수를 활용해서 !!
    //이유 아래 설명
    • SetHp 같은 함수를 쓰는 이유: 
      • 장점: 이 방식을 쓰면 누가 언제 hp 를 고쳤는지 찾기 쉬워진다.
      • 안쓰면 knight.hp 찾아야하는데 더 힘듦.
      • 그래서 hp 를 private 선언해놓고 public 선언된 setter 를 써서 수정한다

     

    클래스 형식변환

    class Player { }
    class Mage: Player { } 
    
    static void EnterGame(Player player){ }
    
    static void Main(string[] args)
    {
    	Mage mage = new Mage();
        EnterGame(mage); 
        // EnterGame 함수는 Player 인스턴스를 인자로 받지만 
        //이것을 상속받은 Mage 인스턴스도 가능하다
    }
    • 요렇게 받았을 때 EnterGame 에서 확인 하는 방법
      • player is Mage
      • player as Mage
        • 요 방법을 더 많이 쓴다. 더 깔끔해서
    static void EnterGame(Player player)
    { 
    	//1번 방법
    	bool isMage = (player is Mage);
        
        //2번 방법
        Mage mage = (player as Mage);
        //player 가 Mage 아니면 mage 는 null인 상태가 된다
    
    }
    
    static void Main(string[] args)
    {
    	Mage mage = new Mage();
        EnterGame(mage); 
    
    }

     

     

    다형성 ( Polymorhism)

    • 부모 & 자식이 같은 이름의 함수를 가지고 있다면?
    • virtual / override 키워드를 사용하여 가상 함수를 만든다  (오버로딩과 헤깔리지 말자)
    • 언제 문제인가? 윗 상황에서도 문제 볼수 있다. 
      • 만약 Player와  Mage 클래스 둘다에서 Move 라는 함수가 있다면, EnterGame 에서 player 인자 받아서 player.Move() 호출하면 Player 클래스의 Move 가 호출 되는 것일까 Mage의 Move 가 호출되는 것일까? ---> 부모의 Move 가 호출되게 됨
      • 해결: 
    class Player
    {
    	public virtual void Move(){ }
    }
    
    class Mage: Player
    {
    	public override void Move() { }
    }
    • 이제 EnterGame 함수안에서 player.Move() 하면 자식의 Move() 가 호출된다
      • 부모 함수를 대체하고 싶지는 않고 기능만 추가 하고싶을 때에는 base 키워드를 써서 추가하자 (아래 예시)
    class Player
    {
    	public virtual void Move(){ }
    }
    
    class Mage: Player
    {
    	public override void Move() 
        { 
        	base.Move(); //부모의 move함수를 먼저 호출한다
            //내용 추가
        }
    }
    • virtual 에는 성능 부하가 있다는 것을 염두
    • 자식의 override를 막으려면 Sealed 라는 키워드를 쓴다 (하지만 거의 안쓰임)

     

    문자열 둘러보기

    string name = "hello";
    
    //찾기
    name.Contains("Hel"); // true, false
    name.indexOf("h"); //0, 없으면 -1
    
    //변형
    name + " world"; //추가
    name.ToLower();
    name.ToUpper();
    name.Replace("h", "k");
    
    //분할
    name.Split(new char[]{' '});
    name.Substring(3) //3부터 잘라 보관

     

    Section 5: TextRPG2

    • 클래스는 따로 빼서 다른 파일로 저장한다
    • include 등 이런거 없이 다른 파일에서 사용할수 있다 (namespace 가 같다는 가정하에)

     

     

    Section 6: 자료 구조 맛보기

    배열

    • 동적. 즉 참조 타입
    int[] a;
    int[] b = new int[5] // 5개 짜리 정수형 배열
    
    //세가지 방법
    int[] scores = new int[5] {10, 20, 30, 40, 50}; //요거 추천
    int[] scores = new int[]{10, 20, 30, 40, 50};
    int[] scores = {10, 20, 30, 40, 50};
    
    
    //iterate
    for (int i = 0; i < 5; i++ ) { } //자칫 index out of range
    
    //그러니 요거나 
    for (int i = 0; i < b.Length; i++) { } 
    
    //요거
    foreach(int score in b){ }

     

    • 다차원 배열
    int[,] arr = new int[2,3]; // [2차원 크기, 1차원 크기]
    
    /// 요런 모양
    ///[x,x,x]
    ///[x,x,x]
    
    int[,] array = new int[2,3]{{1, 2, 3}, {1, 2, 3}};
    • 가변 배열: int[][] a = new int[2][]; (배열의 배열) 

     

    List

    • 배열의 아쉬운 점: 고정 크기 -> 그래서 리스트 (동적 배열)
    using Systems.Collections.Generic 
    
    List<int> list = new List<int>(); //클래스. 참조 형태
    list.Add(1); // 추가방법. list[0] = 2 따위는 안된다. 
    
    //list.Count 로 크기 알 수 있다. 
    
    //삽입 삭제
    list.Insert(index, item);
    list.Remove(item);
    list.RemoveAt(index);
    list.Clear();
    • Insert, remove, removeAt 은 효율성 떨어진다 -> 왜냐면 뒤에 아이템들 모두 이동 시켜야 하니까. 

     

    Dictionary

    • list 의 단점: 찾기 힘들다
    • 사전으로 특정 키-> 벨류 빠르게 찾을 수 있다: hashtable 써서 구현되어 있기 때문이다 (메모리 희생시키고 성능 올린것)
    Dictionary<int, Player> dic = new Dictionary<int, Player>();
    
    dic.Add(key, value);
    dic[5] = new Player(5);
    
    //찾기
    dic.TryGetValue(key, out player); // true, false
    
    dic.Remove(key);
    dic.Clear();

     

    Section 7: 알아두면 유용한 기타 문법

    Generic (일반화)

    • 우선 오브젝트 vs Var
      • 오브젝트는 타입 자체가 object
      • var 는 컴파일러가 알아서 바로바로 변환시킨다
      • string, int,... 가 모두 object를 상속 받기에 가능한것이다. 
      • object는 느리다는 단점. 왜냐면 참조 타입 (heap 에 메모리 할당) 
    Object obj = 3;
    Object obj2 = "hello";
    int num = (int)obj; // 꺼낼시 캐스팅 필요
    • 일반화
      • 클래스의 일반화 'T'를 써서:
        • 각 타입마다 클래스 선언 하지 않아도 된다. 
    class MyList<T>
    {
    	T[] arr = new T[10];
    }
    
    static void Main(string[] args)
    {
    	MyList<int> myList = new MyList<int>();
        MyList<short> myList = new MyList<short>();
        MyList<Player> myList = new MyList<Player>();
    }
    • 함수도 일반화 가능하다
    static void Test<T>(T input) { }
    • 일반화 타입에 조건 추가 가능
    class MyList<T> where T: struct
    { }

     

    Abstract & Interface

    • Abstract: 최상 클래스. abstract 을 붙인 클래스이다 (인스턴스를 만들 수 없다)
      • 함수 틀만 잡고 내용은 정의 하지않고 상속받은 멤버들이 override 를 써서 무조건 정의 하게 한다
      • 문제점: 다중상속 불가 하다
        • c#에서는 다중 상속이 불가 하지만 인터페이스는 여러개 넘길수 있다(마치 다중 상속처럼)
        • 다중 상속 불가한 이유: 죽음의 다이아몬드 같은 문제, 즉 같은 함수가 있을시 호출 충돌 문제 때문에 불가 하게됨.
    absract class  Monster
    {
    	public abstract void Shout(); //abstract 때문에 함수 내용은 정의하지 않는다.
    }
    
    class Orc: Monster
    {
    	public override void Shout(){//정의} //상속받은 멤버는 꼭 Override 로 무조건 정의 해야한다.
    }
    
    class Skeleton: Monster
    {
    	public override void Shout(){//정의} //강요. 강제
    }
    • 해결법: 인터페이스 문법을 사용하여 다중상속처럼 쓰도록 한다. 
      • interface 키워드 사용, 또 I 를 이 클래스 이름 앞에 붙여 다른 클래스와 구분하도록 한다
    interface IFlyable
    {
    	void Fly(); 
    }
    
    class FlyableOrc: Orc, IFlyable // 마치 다중 상속처럼
    {
    	//fly 함수 꼭 정의 필요하다
    }
    • Abstact 보다 interface 가 더 유연하다-> 여러개 인터페이스 동시 사용 가능하니까
      • 왜 자식의 함수 정의 강요 필요한가? 왜 인터페이스 필요한가?
        • 인터페이스를 상속? 받은 애들을 묶음으로 활용 가능 
    IFlyable flyable = new FlyableOrc(); //라던가
    
    static void doFly(Iflyable flyable){ }  //라던가 활용 방법 많음

     

    ⭐️프로퍼티 (객체지향의 은닉성과 관련있다)

    public int HP
    {
    	get { } 
        set { }
    }
    
    
    //위에랑 요거랑 똑같은 것. 더 간소화 해서 쓸 수 있음
    //private, protected 도 붙여 쓸수 있다
    
    public int HP
    {
    	get { return hp; } 
        set { this.hp = value; }
    }
    
    
    knight.hp = 100 // 이때 set 이 사용된다
    int hp = knight.hp // 이때 get 이 사용된다
    • ⭐️⭐️⭐️⭐️⭐️ 더 간결한 방법: 자동 완성 프로퍼티
    public int Hp {get; set;} //c# 7.0 부터 사용 가능해졌다
    
    
    //풀이
    private int Hp;
    
    public int GetHp()
    {
    	return Hp;
    }
    
    public int SetHp(int val)
    {
    	Hp = val;
    }

     

    ⭐️⭐️Delegate ( 대리자) ⭐️⭐️

    • 사용 빈도, 중요도 높음!
    • 함수 자체를 인자로 넘기는 형식 (함수가 아님): callback방식
    • 함수를 수정할 수 없을때 (내가 만든 함수가 아닐때) 도 유용하다
    • delegate chaining 가능
    delegate int OnClicked();
    //delegate 형, 반환: int, 형식의 이름, 입력: void
    //다시한번...함수가 아니고 형식!!!!!!!
    
    
    static void ButtonPressed(OnClicked clickedFunction)
    {
    	clickedFunction();
    }
    
    static int TestDelegate()
    {
      Console.WriteLine("test");
    }
    
    ButtonPressed(TestDelegate); //TestDelegate 는 델리게이트와 같은 입력, 반환 형식이어야 한다.

     

    ⭐️⭐️Event⭐️⭐️

    • delegate의 문제: 아무나 호출 가능하다 (설계적 문제)
    • wrap 하는 방법-> event (구독 신청만 가능하다)
    • 예시) 사용자가 'A'키를 눌렀을때 그 정보가 필요한 함수들에게 눌렸다고 알려주는 형식 (정보를 뿌려주는 형태)
    • Oberserver 패턴: 
    class InputManager
    {
    	public delegate void OnInputKey();
        public event OnInputKey InputKey;
        
        public void Update()
        {
        	if (Console.KeyAvailable == false)
            	return;
            
            ConsoleKeyInfo info = Console.ReadKey();
            if (info.Key == ConsoleKey.A)
            {
            	InPutKey(); // 구독 신청자에게 모두에게 알려준다.
    		}
    	}
    	
    }
    
    
    InputManager inputManger = new InputManager();
    inputManager.InputKey += OnInputTest; // 구독신청
    while (true)
    {
    	inputManager.Update();
    }

     

    Lambda : 일회용 함수

    • delegate keyword 를 사용해서 할수 있다 (무명, 익명 함수)
    • 람다식이 더 간략

     

    Exception

    • try...catch....finally

     

    Reflection

    • x-ray: 런타임떄 클래스의 정보를 빼올수 있다
    • using Reflection; 추가해야함

     

    Nullable

     

     


    Bonus: shortcuts and more

    • Naming Convention (업계)
      • 클래스 인자/필드 들은 클래스 안에서만 쓸떄 _(언더바)나 m_을 앞에 붙여 구분하기도 한다  ex) _value
      • 밖에서도 필요한 필드는 대문자 시작으로 : ex) Value
      • 인터페이스 클래스: I 를 이 클래스 앞에 붙어 구분하도록 한다 ex) IFlyable
    • Shortcuts (Editor)
      • cw + tab + tab 으로 Console.WriteLine() 바로 작성 // Console.Write() 은 줄바꿈없이
    • Shortcuts (Debugging, Mac)
      • Step into: shift + cmd + I / F11 (window)
      • Step over: shift + cmd + o
      • Run: cmd + enter
      • quit run: cmd + shift + enter
      • Build: cmd + b
      • Go to definition: cmd + o
    • Random #
    Random rand = new Random();
    int randValue = rand.Next(0,3) // 0 에서 2 사이의 랜덤값
    
    int choice = Convert.ToInt32(Console.ReadLine()); //사용자 입력값 int 로 치환해서 저장
    • 그 외: 
      • #region / #endregion: 으로 가독성 높이기

    '👩🏻‍💻 c#' 카테고리의 다른 글

    Server  (0) 2023.09.01
    자료구조와 알고리즘  (0) 2023.08.01