C# [1주차]

언어의 특징

  • 객체 지향 프로그래밍 언어

  • 동적 할당된 메모리를 자동으로 회수

  • 변수나 객체의 자료형이 미리 정의되어 있어 데이터 타입에 대한 안전성과 정확성을 보장

  • .NET 프레임워크를 통해 다양한 프로그래밍 언어를 지원함 (-> 애플리케이션 개발 생산성 증가)

변수와 자료형 구조

  • 기본적으로 사용되는 자료형으로 메모리의 크기와 표현 범위가 조금씩 다르다!
자료형 .NET 데이타 타입 크기 (바이트) 범위
sbyte System.SByte 1 -128 ~ 127
byte System.Byte 1 0 ~ 255
short System.Int16 2 -32,768 ~ 32,767
ushort System.UInt16 2 0 ~ 65,535
int System.Int32 4 -2,147,483,648 ~ 2,147,483,647
uint System.UInt32 4 0 ~ 4,294,967,295
long System.Int64 8 -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807
ulong System.UInt64 8 0 ~ 18,446,744,073,709,551,615
float System.Single 4 ±1.5 × 10^-45 ~ ±3.4 × 10^38
double System.Double 8 ±5.0 × 10^-324 ~ ±1.7 × 10^308
decimal System.Decimal 16 ±1.0 × 10^-28 ~ ±7.9 × 10^28
char System.Char 2 유니코드 문자
string System.String variable 유니코드 문자(배)열
bool System.Boolean 1 true 또는 false

이러한 자료구조들을 사용해 메모리를 효율적으로 할당해주며, 값을 정확하게 표현할 수 있다!

  • 추가로 var 를 사용하면, 자료형을 자동으로 지정해준다!

리터럴(literal)

프로그램에서 직접 사용되는 상수 값으로, 소스 코드에 저장되어 있는 것들을 말한다!
이와 같은 리터럴을 이용해 변수에 값을 할당해줄 수 있다. (아래는 타입에 따른 리터럴 예시)

  • 정수형 리터럴
    • 10 (int)
    • 0x10 (16진수 int)
    • 0b10 (2진수 int)
    • 10L (long)
    • 10UL (unsigned long)
  • 부동소수점형 리터럴
    • 3.14 (double)
    • 3.14f (float)
    • 3.14m (decimal)
  • 문자형 리터럴
    • ‘A’ (char)
    • ‘\n’ (개행 문자)
    • ‘\u0022’ (유니코드 문자)
  • 문자열 리터럴
    • “Hello, World!” (string)
    • “문자열 내 “따옴표” 사용하기”
    • @”문자열 내 개행 문자 사용하기”

이를 이용해 변수를 선언/초기화 해줄 수 있다! (+ 형변환)

// 동시 선언, 초기화
int num1, num2, num3;
num1 = num2 = num3 = 10;

// 10은 정수형 값이지만 float형으로 암시적 형변환
float num4 = 10;

// 명시적으로 형변환 (자료형)
int num5 = 5;
long num6 = (long)num5;

연산자

  • 기본적으로 이미 배운 Node.js 와 같으나, 비교에서 “===” 3개의 Equals 가 아닌 “==” 2개를 사용한다!
    (예시 : Node.js: num === 1 && num2 !== 1 / CSharp: num == 1 && num2 != 1 )

연산자 우선순위

기본적으로 다양한 연산자들 사이에서 올바른 계산순서를 위해 우선순위를 가지고 있으며 아래와 같다!

  1. 괄호 (): 괄호로 감싸진 부분은 가장 높은 우선순위로 먼저 계산

  2. 단항 연산자: 단항 연산자들(++, –, +, -, ! 등)은 괄호 다음으로 높은 우선순위를 가짐

  3. 산술 연산자: 산술 연산자들(*, /, %, +, -)은 단항 연산자보다 우선순위가 낮음

  4. 시프트 연산자: 시프트 연산자(«, »)는 산술 연산자보다 우선순위가 낮음

  5. 관계 연산자: 관계 연산자들(<, >, <=, >=, ==, !=)는 시프트 연산자보다 우선순위가 낮음

  6. 논리 연산자: 논리 연산자들(&&, ||)는 관계 연산자보다 우선순위가 낮음

  7. 할당 연산자: 할당 연산자들(=, +=, -=, *=, /= 등)는 논리 연산자보다 우선순위가 낮음

비트 연산자

  • 비트 단위의 연산에서 사용되는 것들이다!
연산자 설명
& (AND) 두 비트 값이 모두 1일 때 1을 반환
(OR) 두 비트 값 중 하나라도 1일 때 1을 반환
^ (XOR) 두 비트 값이 서로 다를 때 1을 반환
~ (NOT) 비트 값의 보수(complement)를 반환
« (왼쪽 시프트) 비트를 왼쪽으로 이동
» (오른쪽 시프트) 비트를 오른쪽으로 이동

문자열

C#에서 문자열을 이용해 할 수 있는 여러 기능들 중, 새로 알게된 부분(Node.js와의 차이)들을 정리해두었다!

// 문자 'H'를 5개로 구성된 문자열 생성
string str2 = new string('H', 5); 

// 이 경우 Node.js의 sort 메서드와 같이 문자열을 비교해 음수(a < b), 동일(a == b), 양수(a > b) 의 값을 반환한다!
string str1 = "Apple";
string str2 = "Banana";
int compare = string.Compare(str1, str2);

// 변환 시 직접 지정을 해줌(암시적 불가)
string str = "123";
int num = int.Parse(str);

// 문자열 포맷팅 방식 (문자열에 변수 값을 할당해주기)
string name = "John";
int age = 30;
string message = string.Format("My name is {0} and I'm {1} years old.", name, age);

string name = "John";
int age = 30;
string message = $"My name is {name} and I'm {age} years old.";

C# [2주차]

이번 주차는 C#의 조건문과 반복문 / 메서드와 구조체 에 대한 설명이 있었다.

조건문과 반복문

기본적으로 조건문과 반복문의 경우 Node.js에서 배웠던 것들과 구조상 거의 동일하였다 (아마도 코드의 기본적인 구조느낌이죠)
그럼에도 차이점이 있는 부분들이 있어 그 부분만 추가로 정리하려한다

// 숫자형 배열 선언
int[] numbers = {1 ,6 , 7, 8, 9};

// <자료구조> 변수명 in 복수형 자료구조
foreach(int number in numbers) {
  Console.WriteLine(number);
}

배열과 컬렉션

C#에서 동일한 데이터 유형을 가지는 데이터 요소들을 한 번에 모아서 다룰 수 있는 구조들을 정리해두었다.

  • 배열과 컬렉션간 크기를 구하는 속성으로 배열 : Length / 컬렉션: Count 을 사용한다!

  • Length는 속성, Count는 속성이지만 메서드처럼 동작

배열

  • 기본적으로 자료구조가 가변적이였던 Node.js와는 달리 직접 배열과 자료형을 선언해줘야 한다는 차이점이 있다!

  • 추가로 메모리 크기를 미리 지정해두어 사용하는 방식이다.

/* 배열 선언
데이터_유형[] 배열_이름;*/
int [] arr;

/* 배열 초기화
배열_이름 = new 데이터_유형[크기];*/
arr = new int[5];
// 암시적 값 초기화 가능
arr = new int[]{1, 2, 3, 4, 5};
arr = {1, 2, 3, 4, 5}

/* 배열을 한 줄로 선언 및 초기화
데이터_유형[] 배열_이름 = new 데이터_유형[크기]; */
int[] arr = new int[5];

/* 배열 요소에 접근
값 = 배열_이름[인덱스]; 
배열_이름[인덱스] = 값; */
int value = arr[0];
arr[0] = value;

/* 다 차원 배열  */

// 2행 3열의 int형 2차원 배열 선언
int[,] array3 = new int[2, 3];  

// 다차원 배열 초기화
array3[0, 0] = 1;
array3[0, 1] = 2;
array3[0, 2] = 3;
array3[1, 0] = 4;
array3[1, 1] = 5;
array3[1, 2] = 6;

// 선언과 함께 초기화
int[,] array2D = new int[,] { { 1, 2, 3}, { 4, 5, 6} };

Collection

이제 배열과 별개로 크기가 가변적인 자료구조들 컬렉션(Collection)들에 대해 알아볼 예정이다!
(아마 이게 Node.js의 가변적인 배열 및 객체들과 유사하지 않나 싶다.)

  1. List

    • 크기가 가변적이며 연속성을 가진 자료 구조
     // 빈 리스트 생성
     List<int> numbers = new List<int>(); 
    
     // 리스트 마지막에 데이터 추가
     numbers.Add(1); 
     numbers.Add(2);
     numbers.Add(3);
     // 리스트에서 제일 처음 발견되는 데이터(2) 삭제
     numbers.Remove(2); 
    
     // 리스트 데이터 출력
     foreach(int number in numbers) {
         Console.WriteLine(number);
     }
    
  2. Dictionary

    • 키와 값으로 구성된 데이터를 저장

    • 중복된 키를 가질 수 없으며, 키와 값의 쌍을 이루어 데이터를 저장 (Map() 같은 건가?)

     using System.Collections.Generic;
    
     // 빈 딕셔너리 생성
     Dictionary<string, int> scores = new Dictionary<string, int>(); 
     // 딕셔너리에 데이터 추가
     scores.Add("Alice", 100); 
     scores.Add("Bob", 80);
     scores.Add("Charlie", 90);
     // 딕셔너리에서 데이터 삭제
     scores.Remove("Bob"); 
    
     // 딕셔너리 데이터 출력
     foreach(KeyValuePair<string, int> pair in scores) {
         Console.WriteLine(pair.Key + ": " + pair.Value);
     }
    
  3. Stack

    • 후입선출(LIFO) 구조를 가진 자료 구조( 봐왔던 거네요!)
     Stack<int> stack1 = new Stack<int>();  // int형 Stack 선언
    
     // Stack에 요소 추가
     stack1.Push(1);
     stack1.Push(2);
     stack1.Push(3);
    
     // Stack에서 요소 가져오기
     int value = stack1.Pop(); // value = 3 (마지막에 추가된 요소)
    
  4. Queue

    • 위와 반대로 선입선출(FIFO) 구조를 가진 자료 구조!
     Queue<int> queue1 = new Queue<int>(); // int형 Queue 선언
    
     // Queue에 요소 추가
     queue1.Enqueue(1);
     queue1.Enqueue(2);
     queue1.Enqueue(3);
    
     // Queue에서 요소 가져오기
     int value = queue1.Dequeue(); // value = 1 (가장 먼저 추가된 요소)
    
  5. HashSet

    • HashSet은 중복되지 않은 요소들로 이루어진 집합
     HashSet<int> set1 = new HashSet<int>();  // int형 HashSet 선언
    
     // HashSet에 요소 추가
     set1.Add(1);
     set1.Add(2);
     set1.Add(3);
    
     // HashSet에서 요소 가져오기
     foreach (int element in set1) {
         Console.WriteLine(element);
     }
    

메서드와 구조체

메서드는 기존에서도 자주 사용해봤기 때문에 이번에 새롭게 알게된 접근제한 / 오버로딩만 간략하게 정리하려 한다!
( 사실 이전에 조금 공부한 C나 C++을 통해 개념을 조금은 알고는 있었다. )

  • Static이란 프로그램이 시작할 때 stack 메모리에 등록되어 인스턴스가 호출되지 않아도 실행가능토록 해주는 구분자이다!
    (=> 필드나 메소드가 클래스의 인스턴스가 아닌 클래스 자체에 소속되도록 함 )
  1. 접근제한

    • 객체 내 메서드/변수에 접근할 수 있는 범위를 지정하는 것! (아래와 같이 여러 종류가 있다!)

    • Public: 누구든 접근할 수 있다!

    • Internal: 동일 네임스페이스 내에서 접근 가능

    • Protect: 상속받거나 메서드/변수를 소유한 객체에서만 접근할 수 있다!

    • Private: 메서드/변수를 소유한 객체에서만 접근할 수 있다!

  2. 오버로딩

    • 동일한 이름의 메서드를 다양한 매개변수 목록으로 다중 정의하는 개념!

    • 메서드의 기능이나 작업은 동일하지만 입력값에 따라 다르게 동작해야 할 때 사용

     void PrintMessage(string message) {
         Console.WriteLine("Message: " + message);
     }
    
     void PrintMessage(int number) {
         Console.WriteLine("Number: " + number);
     }
    
     // 문자열 매개변수를 가진 메서드 호출
     PrintMessage("Hello, World!");  
     // 정수 매개변수를 가진 메서드 호출
     PrintMessage(10);  
    

구조체(struct)는 이번에 처음 보게되었지만, 조금 더 살펴보니 Class보다 조금 더 근본인 자료 구조인 것 같다고 생각되었다!

  1. 구조체는 기본적으로 Public으로 정의되지만, Class는 Private으로 정의됨

  2. 구조체는 값 타입으로 스택 메모리에 생성되고, 클래스는 참조 타입으로 힙 메모리에 생성됨

  3. 구조체는 상속이 불가능하지만, 클래스는 상속이 가능

  4. 구조체는 안에 내용이 비어있으면 안 되지만, 클래스는 비어있어도 됨

// 구조체 선언
struct Person
{
    public string Name;
    public int Age;

    public void PrintInfo() {
        Console.WriteLine($"Name: {Name}, Age: {Age}");
    }
}

// 구조체 활용
Person person1;
person1.Name = "John";
person1.Age = 25;
person1.PrintInfo();

한줄 평 + 개선점

  • 기존 언어와의 유사점이 많아 이해는 빠르지만, 많이 사용해보면서 익혀야 겠다! (알고리즘 코드 테스트 on!)