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 )
연산자 우선순위
기본적으로 다양한 연산자들 사이에서 올바른 계산순서를 위해 우선순위를 가지고 있으며 아래와 같다!
-
괄호 (): 괄호로 감싸진 부분은 가장 높은 우선순위로 먼저 계산
-
단항 연산자: 단항 연산자들(++, –, +, -, ! 등)은 괄호 다음으로 높은 우선순위를 가짐
-
산술 연산자: 산술 연산자들(*, /, %, +, -)은 단항 연산자보다 우선순위가 낮음
-
시프트 연산자: 시프트 연산자(«, »)는 산술 연산자보다 우선순위가 낮음
-
관계 연산자: 관계 연산자들(<, >, <=, >=, ==, !=)는 시프트 연산자보다 우선순위가 낮음
-
논리 연산자: 논리 연산자들(&&, ||)는 관계 연산자보다 우선순위가 낮음
-
할당 연산자: 할당 연산자들(=, +=, -=, *=, /= 등)는 논리 연산자보다 우선순위가 낮음
비트 연산자
- 비트 단위의 연산에서 사용되는 것들이다!
연산자 | 설명 |
---|---|
& (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의 가변적인 배열 및 객체들과 유사하지 않나 싶다.)
-
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); }
-
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); }
-
-
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 (마지막에 추가된 요소)
-
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 (가장 먼저 추가된 요소)
-
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 메모리에 등록되어 인스턴스가 호출되지 않아도 실행가능토록 해주는 구분자이다!
(=> 필드나 메소드가 클래스의 인스턴스가 아닌 클래스 자체에 소속되도록 함 )
-
접근제한
-
객체 내 메서드/변수에 접근할 수 있는 범위를 지정하는 것! (아래와 같이 여러 종류가 있다!)
-
Public: 누구든 접근할 수 있다!
-
Internal: 동일 네임스페이스 내에서 접근 가능
-
Protect: 상속받거나 메서드/변수를 소유한 객체에서만 접근할 수 있다!
-
Private: 메서드/변수를 소유한 객체에서만 접근할 수 있다!
-
-
오버로딩
-
동일한 이름의 메서드를 다양한 매개변수 목록으로 다중 정의하는 개념!
-
메서드의 기능이나 작업은 동일하지만 입력값에 따라 다르게 동작해야 할 때 사용
void PrintMessage(string message) { Console.WriteLine("Message: " + message); } void PrintMessage(int number) { Console.WriteLine("Number: " + number); } // 문자열 매개변수를 가진 메서드 호출 PrintMessage("Hello, World!"); // 정수 매개변수를 가진 메서드 호출 PrintMessage(10);
-
구조체(struct)는 이번에 처음 보게되었지만, 조금 더 살펴보니 Class보다 조금 더 근본인 자료 구조인 것 같다고 생각되었다!
-
구조체는 기본적으로 Public으로 정의되지만, Class는 Private으로 정의됨
-
구조체는 값 타입으로 스택 메모리에 생성되고, 클래스는 참조 타입으로 힙 메모리에 생성됨
-
구조체는 상속이 불가능하지만, 클래스는 상속이 가능
-
구조체는 안에 내용이 비어있으면 안 되지만, 클래스는 비어있어도 됨
// 구조체 선언
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!)