list
복습
VS에서 주석처리 -> ctrl + K ,C / ctrl + K ,U [주석해제]
1. 사칙연산 계산기
- 입출력, 문자열+변수 동시출력 , 조건문(switch)
using System;
class Program1
{
static void Main()
{
Console.Write("type num1 (int): ");
int first_num = Convert.ToInt32 (Console.ReadLine());
Console.Write("type num2 (int): ");
int second_num = Convert.ToInt32 (Console.ReadLine());
Console.Write("type operator (+ , - ,* , / ): ");
string operate = Console.ReadLine();
double result = 0;
switch (operate)
{
case "+":
result = first_num+ second_num; break;
case "-":
result = first_num - second_num; break;
case "*":
result = first_num * second_num; break;
case "/":
result = first_num / second_num; break;
}
Console.WriteLine($" {first_num}{operate}{second_num} = {result}");
}
}
2. 구구단
- 반복문 (while) , 조건문(if) , 반복&조건 제어문(break)
using System;
class Program1 {
static void Main()
{
int goo = 1;
Console.WriteLine("Let's study googoo....!");
while (true)
{
Console.WriteLine($"{goo}단 입니다....ㅃ!");
for (int i = 1; i < 10; i++)
{
Console.Write($"{goo}*{i} = {goo*i} ");
}
Console.WriteLine();
goo++;
if (goo == 10 ) break;
}
}
}
3. 이중 for문을 이용한 피라미드
using System;
class Program3
{
static void Main()
{
// 칸 수를 입력 받아서 피라미드를 만들어 보아용
Console.Write("피라미드는 몇칸 인가요??? : ");
int layer = Convert.ToInt32(Console.ReadLine());
for (int i =0;i<layer ;i++)
{
int iteration1 = layer - i;
int iteration2 = 2 * i + 1;
for (int j =0; j< iteration1;j++) //공백 찍기
{
Console.Write(" ");
}
for (int j = 0; j < iteration2; j++) // 별찍기
{
Console.Write("*");
}
Console.WriteLine();
}
}
}
4. 2차원 항등행렬 출력
1.
using System;
class Program4
{
static void Main()
{
//[3,3] 2차원 배열의 행,열이 같으면 1 ,다르면 0 출력. *항등행렬
int[,] arr = new int [2, 2];
//Console.WriteLine(arr.Length); // 크기는 4이다.
for (int i = 0; i < 2; i++)
{
for (int j =0;j<2 ;j++)
{
if ( i==j)
{
Console.Write("1 ");
}
else
{
Console.Write("0 ");
}
}
Console.WriteLine();
}
}
}
2. [참고] - 차수(또는 차원)를 구분하여 길이(length)를 알아내는 방법
- `GetLength()` 메서드를 사용함.
- 다차원 배열의 각 차원의 길이를 알아낼 수 있습니다.
static void Main()
{
int[,] arr2 = new int[1, 2];
Console.WriteLine(arr2.GetLength(0));
Console.WriteLine(arr2.GetLength(1));
int[,,] arr3 = new int[1, 2, 3];
Console.WriteLine(arr3.GetLength(0));
Console.WriteLine(arr3.GetLength(1));
Console.WriteLine(arr3.GetLength(2));
}
배열
[배열]
- 같은 데이터 형식을 가진 데이터 여러 개를 저장하는 데이터 구조
- 배열은 순서가 있는 요소들의 집합으로, 값 하나는 요소(element) 또는 항목(item)으로 표현한다.
- 배열은 메모리의 연속된 공간을 미리 할당하고, 대괄호([])와 0부터 시작하는 정수형 인덱스를 사용해 접근하는 구조.
- 배열 선언 시에는 new 키워드로 생성 후 사용할 수 있다.
[배열 선언]
1. 변수 선언과 배열 선언 차이
- 배열은 자료형과 변수 사이에 대괄호 작성.
- new 키워드 , 정수형 인덱스 [요소의 갯수]
2. [참고]
3. 한줄 선언 방식
* numbers 라는 int 데이터 요소를 갖는 배열을 생성. 배열 요소의 갯수는 3개.
4. 할당
1)
2)
3) 할당시 변수, 대입연산자 생략
static void Main()
{
int[,,] arr = new int[3, 3, 3] //[error]세미콜론 X
//[error] 대입 연산자는 컴파일 에러
arr ={ { { 1,2,3},{ 1,2,3},{ 1,2,3} },{ { 1,2,3},{ 1,2,3},{ 1,2,3} },{ { 1,2,3} ,{ 1,2,3} ,{ 1,2,3} } };
//[correct] 중괄호로 감싼 값만 준다.
{ { { 1,2,3},{ 1,2,3},{ 1,2,3} },{ { 1,2,3},{ 1,2,3},{ 1,2,3} },{ { 1,2,3} ,{ 1,2,3} ,{ 1,2,3} } };
}
}
5. 다차원 배열
- 대괄호 내부에 콤마 갯수로 차원을 나타낸다.
- [참고] 3차원 배열에서 특정 인덱스 값을 출력 하려면?
static void Main()
{
int[,,] arr3 = new int[2,2,2] { { { 1, 2 }, { 3, 4 } },{ { 5, 6 }, { 7, 8 } } };
Console.Write($"{arr3[0, 0, 0]} " );
Console.Write($"{arr3[0, 0, 1]} " );
Console.Write($"{arr3[0, 1, 0]} " );
Console.Write($"{arr3[0, 1, 1]} " );
Console.Write($"{arr3[1, 0, 0]} " );
Console.Write($"{arr3[1, 0, 1]} " );
Console.Write($"{arr3[1, 1, 0]} " );
Console.Write($"{arr3[1, 1, 1]} " );
}
1. 기본 사용
using System;
class Pro
{
static void Main()
{
int[] arr;
arr=new int[3];
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
//for 문을 이용한 출력
// 배열 크기 넘어가면 에러
for (int i=0; i<3;i++)
{
Console.WriteLine($"{i}번째 인덱스의 값 : {arr[i]}");
}
//foreach - 배열의 전체 요소 순회
foreach (int i in arr)
{
Console.WriteLine("{0}",i);
}
}
}
2. 배열 크기 구하기 [Length]
* [배열이름.Length] 형태로 Length 속성을 사용하면 된다.
static void Main()
{
char[] chars = { 'a', 'b', 'c' };
Console.WriteLine(chars.Length);
for (int i = 0; i<chars.Length;i++)
{
Console.WriteLine(chars[i]);
}
}
3. 2차원 배열+ 이중 for문으로 배열요소 출력
using System;
class Pro
{
static void Main()
{
int[,] arr = new int[2, 3]; // 배열 요소 갯수
// 0 0 0
// 0 0 0
arr[0, 0] = 1;
arr[0, 1] = 2;
arr[0, 2] = 3;
arr[1, 0] = 2;
arr[1, 1] = 3;
arr[1, 2] = 4;
for (int i =0;i<2 ;i++)
{
for (int j =0;j<3 ;j++)
{
Console.WriteLine(arr[i,j]);
}
Console.WriteLine();
}
}
}
4. 합계 평균 구하기
using System;
class Pro
{
static void Main()
{
int[,] arr = { { 100, 75, 0, 0 }, { 48, 80, 0, 0 }, { 90, 100, 0, 0 } };
for (int i = 0; i < 3; i++)
{
arr[i, 2] = arr[i, 0] + arr[i, 1];
arr[i, 3] = arr[i, 2] / 2;
}
Console.WriteLine("mean value : ");
for (int i = 0;i<3 ;i++)
{
for (int j =0;j<4 ;j++)
{
Console.WriteLine($"{arr[i,j],4} ");
}
Console.WriteLine();
}
}
}
객체 지향 프로그래밍
[절차 지향 프로그래밍]
- 물이 위에서 아래로 흐르는 것처럼 순차적인 처리가 중요시 되는 프로그래밍 기법
- 프로그램 전체가 유기적으로 연결되어있다
- 대표적인 절차지향 언어에는 C언어가 있음
- 장점 : 컴퓨터의 작업 처리 방식과 유사해 객체지향 언어보다 빨리 처리됨(시간적으로 유리함)
- 단점 : 유지보수가 어려움, 코드 순서 변경 시 동일한 결과를 보장하지 않음
[객체 지향 프로그래밍]
- 좀 더 나은 프로그램을 만들기 위한 프로그래밍 패러다임
- 로직을 상태 와 행위로 이루어진 객체로 만드는 것
- 어떠한 큰 카테고리의 부모 클래스(객체, 모델)을 기준으로 그로 인해 파생될 수 있는 하위 클래스를 정의
- 부모 객체로부터 기본적인 스펙(상태), 기능(행위)를 상속 받아 자식에서 그대로 사용하거나 확장 가능
[참고]
1. [객체지향] https://universitytomorrow.com/15
2. [객체지향] https://youtu.be/fECCYukfVok
3.
[클래스 선언 & 객체 생성, 생성자]
using System;
using System.Threading;
namespace Console1
{
// "Object"라는 이름의 클래스를 정의합니다 (주의: 이 클래스명은 C# 내장 Object 클래스와 충돌합니다)
class Object
{
public string prop1; // "prop1"이라는 public 문자열 속성을 선언합니다
public string prop2; // "prop2"라는 public 문자열 속성을 선언합니다
// "Method1"이라는 public 메서드를 정의합니다
public void Method1()
{
Console.WriteLine("this is public Method1"); // 콘솔에 메시지를 출력합니다
}
}
// "Program"이라는 내부 클래스를 정의합니다
internal class Program
{
static void Main()
{
// "Object" 클래스의 인스턴스인 "obj1"을 생성합니다
Object obj1 = new Object();
// "obj1"의 "prop1" 속성 값을 "value1"로 설정합니다
obj1.prop1 = "value1";
// "obj1"의 "prop2" 속성 값을 "value2"로 설정합니다
obj1.prop2 = "value2";
// "obj1"의 "Method1" 메서드를 호출합니다
obj1.Method1();
// "obj1"의 "prop1"과 "prop2" 속성 값을 출력합니다
Console.WriteLine($"prop1의 값: {obj1.prop1}, prop2의 값: {obj1.prop2}\n");
// "Object" 클래스의 또 다른 인스턴스인 "obj2"를 생성합니다
Object obj2 = new Object();
// "obj2"의 "prop1" 속성 값을 "value3"으로 설정합니다
obj2.prop1 = "value3";
// "obj2"의 "prop2" 속성 값을 "value4"로 설정합니다
obj2.prop2 = "value4";
// "obj2"의 "Method1" 메서드를 호출합니다
obj2.Method1();
// "obj2"의 "prop1"과 "prop2" 속성 값을 출력합니다
Console.WriteLine($"prop1의 값: {obj2.prop1}, prop2의 값: {obj2.prop2}\n");
// 1초 동안 실행을 일시 중지합니다
Thread.Sleep(1000);
}
}
}
@ 1
using System;
using System.Drawing;
namespace Console1
{
class Cat
{
public Cat()
{ Name= ""; Color= ""; }
public Cat(string name , string color) {
Name = name; Color = color;
}
public string Name;
public string Color;
}
internal class Program
{
static void Main(string[] args)
{
Cat test = new Cat();
test.Color = "White";
test.Name= "test1";
Console.WriteLine($"{test.Name} , {test.Color}");
Cat test2 = new Cat();
test2.Color = "red";
test2.Name = "test2";
Console.WriteLine($"{test2.Name} , {test2.Color}");
}
}
}
@2
using System;
namespace Console1
{
class Car
{
private static int count;
private string name;
public Car(string name)
{
this.name = name;
count++;
}
public static void PrintCount()
{
Console.WriteLine(" Number of cars: {0}" , count);
}
}
class Program
{
static void Main(string[] args)
{
Car.PrintCount();
Car car1 = new Car("test1");
Car car2 = new Car("test2");
Car car3 = new Car("test3");
Car.PrintCount();
Console.ReadLine();
}
}
}
@3 - 상속
using System;
namespace Console1
{
class Player
{
public int id;
public int hp;
public int attack;
public Player()
{
Console.WriteLine("Player 생성자 호출");
}
}
class Model1 : Player
{
public Model1()
{
Console.WriteLine("Model1 생성자 호출");
}
}
class Model2 : Player
{
}
internal class Program
{
static void Main(string[] args)
{
Model1 model1 = new Model1();
}
}
}
[@3 참고]
위 코드는 C# 언어로 작성된 간단한 콘솔 응용 프로그램입니다. 각 줄을 하나씩 자세히 설명하겠습니다.
1. `using System;`
- `System` 네임스페이스를 사용한다는 의미로, 기본적인 시스템 라이브러리에 접근할 수 있도록 해줍니다.
3. `namespace Console1`
- `Console1`이라는 이름의 네임스페이스를 정의합니다. 네임스페이스는 관련된 클래스들을 그룹화하여 이름 충돌을 방지하는 역할을 합니다.
5. `class Player`
- `Player` 클래스를 정의합니다. 이 클래스에는 `id`, `hp`, `attack`라는 세 개의 멤버 변수가 있습니다.
11. `public Player()`
- `Player` 클래스의 생성자를 정의합니다. 생성자는 해당 클래스의 인스턴스를 만들 때 호출되는 메서드로, 여기서는 "Player 생성자 호출"이라는 메시지를 출력합니다.
17. `class Model1 : Player`
- `Player` 클래스를 상속하여 새로운 클래스 `Model1`을 정의합니다. `:` 다음에 오는 `Player`는 `Model1`이 `Player` 클래스를 상속한다는 것을 의미합니다.
22. `public Model1()`
- `Model1` 클래스의 생성자를 정의합니다. 생성자는 해당 클래스의 인스턴스를 만들 때 호출되는 메서드로, 여기서는 "Model1 생성자 호출"이라는 메시지를 출력합니다.
28. `class Model2 : Player`
- `Player` 클래스를 상속하여 새로운 클래스 `Model2`를 정의합니다. `Model2` 클래스에는 생성자가 없으므로, 기본적으로 `Player`의 생성자를 상속받습니다.
32. `internal class Program`
- `Program` 클래스를 정의합니다. `internal` 접근 제한자는 같은 어셈블리(프로젝트) 내부에서만 접근 가능하도록 합니다.
34. `static void Main(string[] args)`
- C# 프로그램의 시작점을 나타내는 `Main` 메서드를 정의합니다. 프로그램이 실행되면 이 메서드가 호출됩니다. `string[] args`는 프로그램 실행 시 전달되는 커맨드 라인 인수를 나타냅니다.
36. `Model1 model1 = new Model1();`
- `Model1` 클래스의 인스턴스 `model1`을 생성합니다. 이 때 `Model1`의 생성자가 호출되며, 먼저 `Player`의 생성자가 호출되고, 그 다음에 `Model1`의 생성자가 호출됩니다. 따라서 콘솔에는 다음과 같이 출력될 것입니다:
```
Player 생성자 호출
Model1 생성자 호출
```
코드는 `Player` 클래스를 상속하여 `Model1` 클래스와 `Model2` 클래스를 만들고, `Main` 메서드에서 `Model1`의 인스턴스를 생성하는 간단한 예제입니다.
예외(Exception)
1. System.Exception 클래스 , Try-Catch 구문
using System;
namespace HelloWorld
{
class MainApp
{
static void Main(string[] args)
{
int[] arr = { 1,2,3};
try
{
for (int i =0;i<5 ;i++)
{
Console.WriteLine("{0}",arr[i]);
}
}
catch(System.Exception ex)
{
Console.WriteLine ("예외 발생: {0}",ex.Message);
}
}
}
}
2. Throw 키워드
using System;
namespace Sample
{
internal class Program
{
static void Func1(int arg)
{
if (arg < 10)
{
Console.WriteLine($"arg: {arg}");
}
else throw new Exception("throw exception.");
}
static void Main(string[] args)
{
try {
Func1(11);
}
catch (Exception e) {
Console.WriteLine("출력 , {0}",e.Message);
}
}
}
}
@ 대리자 , 위임 Delegate
- 메소드의 참조를 담는 타입으로, 메소드를 변수처럼 사용할 수 있게 해 줌
using System;
namespace Console1
{
delegate void Calculator(int a, int b);
internal class Program
{
static void Plus(int a , int b)
{
Console.WriteLine(a+b);
}
static void Minus(int a, int b)
{
Console.WriteLine(a-b);
}
static void PrintResult(Calculator calcFunc, int a, int b)
{
calcFunc(a, b);
}
static void Main(string[] args)
{
PrintResult(Plus, 11, 7);
PrintResult(Minus, 3, 24);
}
}
}
[참고] - 함수에 인수로 콜백함수를 전달.....
`delegate`는 C#에서 콜백 함수를 구현하는 방법 중 하나입니다. 콜백 함수란, 어떤 함수가 실행 중에 다른 함수를 호출하고자 할 때, 호출할 함수를 인수로 전달하여 실행하는 것을 말합니다. `delegate`를 사용하면 함수를 객체로 취급하여 다른 함수에게 전달하고, 해당 함수가 필요한 시점에 호출할 수 있습니다.
위의 코드에서 `Calculator` 델리게이트는 `void` 반환형을 가지고, 두 개의 `int` 매개변수를 가지는 함수를 가리키는 델리게이트입니다. `PrintResult` 메서드는 `Calculator` 델리게이트를 인수로 받아서 해당 델리게이트가 가리키는 함수를 호출합니다.
`Main` 메서드에서는 `PrintResult` 메서드를 호출하는데, 첫 번째 인수로 `Plus` 함수를 전달하고 두 번째 인수로 `Minus` 함수를 전달합니다. 이렇게 하면 `PrintResult` 메서드에서는 `calcFunc(a, b)`를 통해 전달된 함수를 실행하게 됩니다.
따라서 위 코드에서는 `Plus` 함수와 `Minus` 함수를 `PrintResult` 메서드에 콜백 함수로 전달하여 각각의 결과를 출력합니다.
델리게이트를 사용하지 않고 다른 방법으로 함수에 인수로 콜백 함수를 전달하는 것은 C#의 델리게이트를 이용하는 것이 가장 일반적이고 권장되는 방법입니다. 하지만 C# 7.0부터는 람다 표현식을 사용하여 간단한 콜백 함수를 직접 인라인으로 전달할 수도 있습니다. 예를 들면:
```csharp
static void Main(string[] args)
{
PrintResult((a, b) => Console.WriteLine(a + b), 11, 7);
PrintResult((a, b) => Console.WriteLine(a - b), 3, 24);
}
```
위와 같이 람다 표현식을 사용하면 별도의 델리게이트를 정의하지 않고도 간편하게 콜백 함수를 전달할 수 있습니다. 람다 표현식은 익명 함수를 생성하여 인라인으로 사용하는 방식으로, 간단한 콜백 함수를 전달할 때 편리하게 사용할 수 있습니다.
@ 이벤트
- 객체에 대한 특정 액션이 발생할 때 다른 객체에게 알리는 매커니즘
- number가 3의 배수일 때 이벤트 발생
using System;
namespace Console1
{
delegate void EventHandler(string message);
class testHandle
{
public event EventHandler Something;
public void Func1(int number)
{
int temp = number % 3;
if (temp ==0)
{
Something($"number = {number} ");
}
}
}
internal class Program
{
static void Test1(string message)
{
Console.WriteLine(message);
}
static void Main(string[] args)
{
testHandle testNoti = new testHandle();
testNoti.Something += new EventHandler(Test1);
for (int i =1;i<30 ;i++)
{
testNoti.Func1(i);
}
}
}
}
파일 정보와 디렉토리 정보
[인스턴스 메소드와 정적 메소드 개념&차이점]
인스턴스 메소드와 정적 메소드는 객체 지향 프로그래밍에서 사용되는 두 가지 유형의 메소드입니다. 각각의 차이점과 개념에 대해 설명하겠습니다:
1. 인스턴스 메소드 (Instance Method):
- 인스턴스 메소드는 클래스의 인스턴스(객체)에 속하는 메소드로, 해당 클래스의 객체를 생성해야만 호출할 수 있습니다.
- 인스턴스 메소드는 객체의 상태(멤버 변수)에 접근하거나 수정할 수 있습니다.
- 주로 객체의 특정 동작(메소드)을 구현하거나 객체의 상태를 조작하는 작업을 수행합니다.
- 인스턴스 메소드는 일반적으로 `public`, `private`, `protected` 등의 접근 제한자를 가질 수 있습니다.
- 인스턴스 메소드는 객체를 생성한 후에 그 객체에 대해 호출할 수 있으며, 객체마다 각자의 상태를 유지합니다.
2. 정적 메소드 (Static Method):
- 정적 메소드는 클래스에 속하는 메소드로, 객체를 생성하지 않고도 클래스 이름을 통해 직접 호출할 수 있습니다.
- 정적 메소드는 객체의 상태에 접근할 수 없습니다. 즉, 멤버 변수를 사용하지 않거나 수정하지 않습니다.
- 주로 유틸리티 메소드나 공통적인 기능을 구현하는데 사용됩니다.
- 정적 메소드는 반드시 `static` 키워드로 선언되어야 하며, 인스턴스 메소드와 달리 `this` 키워드를 사용할 수 없습니다.
- 정적 메소드는 동일한 클래스의 모든 객체에 대해 동일한 결과를 반환하거나 동작을 수행합니다.
간단히 말해, 인스턴스 메소드는 객체의 상태에 접근할 수 있으며 객체마다 다른 동작을 수행할 수 있습니다. 반면에 정적 메소드는 객체와 무관하게 클래스의 기능을 수행하며 객체의 상태에 영향을 주지 않습니다. 이로 인해 정적 메소드는 객체를 생성하지 않고도 호출할 수 있으며, 클래스 레벨의 기능을 제공하는 데 유용합니다.
1. fileInfo 메서드를 이용한 파일 생성및 파일 정보 콘솔출력
using System;
using System.IO;
namespace Sample
{
internal class Program
{
static void Main(string[] args)
{
string filepath = @"C:\Users\user\Desktop\fileInfo.txt";
FileInfo fileInfo = new FileInfo(filepath);
// 파일이 존재하지 않는 경우에만 파일 생성
if (!fileInfo.Exists)
{
// Create 메서드를 사용하여 새 파일 생성
using (FileStream fs = fileInfo.Create())
{
// 파일 생성 후, 필요한 작업 수행
// 이 부분에 파일에 데이터를 쓰거나 다른 작업을 수행할 수 있습니다.
}
Console.WriteLine("파일이 성공적으로 생성되었습니다.");
}
else
{
Console.WriteLine("동일한 이름의 파일이 이미 존재합니다.");
}
Console.WriteLine($"File name: {fileInfo.Name}");
Console.WriteLine($"File Length: {fileInfo.Length}");
Console.WriteLine($"File CreationTime: {fileInfo.CreationTime}");
Console.WriteLine($"File LastAccessTime: {fileInfo.LastAccessTime}");
Console.WriteLine($"File LastWriteTime: {fileInfo.LastWriteTime}");
}
}
}
2. DirectoryInfo
using System;
using System.IO;
namespace Sample
{
internal class Program
{
static void Main(string[] args)
{
string dirPath = @"C:\Users\user\Desktop";
DirectoryInfo dirInfo = new DirectoryInfo(dirPath);
Console.WriteLine($"디렉토리 이름: {dirInfo.Name}");
Console.WriteLine($"디렉토리내 파일 갯수 : {dirInfo.GetFiles().Length}");
Console.WriteLine($"디렉토리 갯수 : {dirInfo.GetDirectories().Length}");
Console.WriteLine($"엄마 디렉토리 : {dirInfo.Parent.FullName}");
Console.WriteLine($"루트 디렉토리 : {dirInfo.Root.FullName}");
/*
디렉토리 이름: Desktop
디렉토리내 파일 갯수 : 11
디렉토리 갯수 : 3
엄마 디렉토리 : C:\Users\user
루트 디렉토리 : C:\
*/
}
}
}
`StreamWriter`와 `StreamReader`, 그리고 `File`, `FileInfo`, `Directory`, `DirectoryInfo` 클래스는 모두 파일과 디렉토리와 관련된 작업을 수행하기 위해 .NET 프레임워크에서 제공하는 클래스들입니다. 각 클래스의 구체적인 차이점은 다음과 같습니다:
1. `StreamWriter`와 `StreamReader`:
- `StreamWriter`: 파일에 텍스트 데이터를 쓰기 위한 클래스입니다. 파일을 생성하거나 기존 파일에 데이터를 추가하여 파일에 문자열이나 텍스트 데이터를 씁니다.
- `StreamReader`: 파일로부터 텍스트 데이터를 읽기 위한 클래스입니다. 파일의 내용을 읽어와서 문자열이나 텍스트 데이터를 읽을 수 있습니다.
2. `File` 클래스와 `FileInfo` 클래스:
- `File` 클래스: 정적 메서드로 파일과 관련된 작업을 수행하는 클래스입니다. 파일의 존재 여부를 확인하거나 파일을 생성, 복사, 삭제, 이동 등의 파일 조작 작업을 수행합니다.
- `FileInfo` 클래스: `File` 클래스와 유사한 기능을 하는데, 이 클래스는 인스턴스 메서드를 사용하여 파일과 관련된 작업을 수행합니다. `FileInfo` 클래스를 사용하면 파일의 속성을 더 편리하게 가져올 수 있습니다.
3. `Directory` 클래스와 `DirectoryInfo` 클래스:
- `Directory` 클래스: 정적 메서드로 디렉토리와 관련된 작업을 수행하는 클래스입니다. 디렉토리의 존재 여부를 확인하거나 디렉토리를 생성, 삭제, 이동 등의 디렉토리 조작 작업을 수행합니다.
- `DirectoryInfo` 클래스: `Directory` 클래스와 유사한 기능을 하는데, 이 클래스는 인스턴스 메서드를 사용하여 디렉토리와 관련된 작업을 수행합니다. `DirectoryInfo` 클래스를 사용하면 디렉토리의 속성을 더 편리하게 가져올 수 있습니다.
간단히 말해, `StreamWriter`와 `StreamReader`는 파일에 텍스트 데이터를 쓰고 읽는 데 사용되며, `File` 클래스와 `FileInfo` 클래스는 파일의 생성, 복사, 삭제, 이동 등의 파일 조작 작업을 수행하고, `Directory` 클래스와 `DirectoryInfo` 클래스는 디렉토리와 관련된 작업을 수행합니다. 인스턴스 메서드를 사용하는 `FileInfo` 클래스와 `DirectoryInfo` 클래스는 해당 파일과 디렉토리의 속성을 더 쉽게 가져올 수 있습니다.
스레드(Thread)
- 각각의 작업(task)은 스레드(thread)라고 불림
- 다중 스레딩 : 하나의 프로그램이 동시에 여러 가지 작업을 할 수 있도록 하는 것
- 보통 프로세스가 생성되면 스레드도 함께 생성되는데 부모 프로세스의 자원을 공유하기 때문에 새로 복사해올 필요가
없어 프로세스의 생성과 종료보다 오버헤드가 적음
- 또 스레드 하나가 상태를 바꾼다고 해서 다른 스레드나 부모 프로세스의 상태 변화에 영향을 끼치지 않음
- 스레드 각각은 독립적으로 실행되나 자원만 함께 같이 사용하는 것
[참고]
https://gmlwjd9405.github.io/2018/09/14/process-vs-thread.html
[OS] 프로세스와 스레드의 차이 - Heee's Development Blog
Step by step goes a long way.
gmlwjd9405.github.io
1. 스레드 시작하기
using System;
using System.Threading;
namespace Sample
{
internal class Program
{
static void Func()
{
for (int i =0; i<5;i++)
{
Console.WriteLine($"Func {i}번째 ");
}
}
static void Main(string[] args)
{
Thread t1 = new Thread(new ThreadStart(Func));
t1.Start(); //스레드 시작
t1.Join(); // 스레드 종료 대기
}
}
}
2. 인터럽트 : 스레드 임의 종료 [불가피하게 스레드를 강제로 종료시켜야 하는 경우]
* Interrupt 메서드로 스레드 종료시킨다.
using System;
using System.Threading;
namespace Sample
{
internal class Program
{
static void Func()
{
for (int i = 0; i < 1000; i++)
{
Console.WriteLine($" Func {i}번째");
Thread.Sleep(10);
}
}
static void Main(string[] args)
{
Thread t1 = new Thread(new ThreadStart(Func));
t1.Start(); //start
t1.Interrupt(); // 스레드 취소(종료)
t1.Join(); // 스레드 종료 대기
}
}
}
[참고]
스레드의 상태는 실행 중인 `Running`, 실행 대기 중인 `WaitSleepJoin`, 블록된 `Blocked`, 정지된 `Stopped` 등 다양한 상태를 가질 수 있습니다. 이 중에서 스레드가 `Running` 상태일 때 `Interrupt()` 메서드를 호출하고, 해당 스레드가 `WaitSleepJoin` 상태가 되면 `ThreadInterruptedException`이 발생할 수 있습니다.
여기서 `Interrupt()` 메서드는 `Thread` 클래스에 정의된 메서드로, 다른 스레드를 인터럽트(Interrupt)하여 해당 스레드를 깨우고 정지된 상태에서 벗어나게 합니다. 이는 해당 스레드가 `Wait`, `Sleep`, `Join` 등의 메서드를 호출하여 실행을 대기하는 상태일 때 유용하게 사용될 수 있습니다.
스레드가 `WaitSleepJoin` 상태라는 것은 해당 스레드가 `Wait`, `Sleep`, `Join` 등의 메서드로 실행을 일시 중지하고 다른 스레드의 작업이 끝날 때까지 기다리는 상태를 의미합니다. 이때, `Interrupt()` 메서드가 호출되면 해당 스레드는 대기 상태를 벗어나 `ThreadInterruptedException`을 발생시키며, 실행 대기 상태로 돌아가게 됩니다.
`ThreadInterruptedException`은 스레드가 `WaitSleepJoin` 상태에서 `Interrupt()` 메서드로 인해 깨어날 때 발생하는 예외입니다. 이 예외는 스레드가 정상적으로 실행되지 않고 일시 중지 상태에서 강제로 깨어난 경우에 발생하며, 해당 예외를 처리하여 스레드의 흐름을 관리할 수 있습니다.
이렇게 `Interrupt()` 메서드와 `ThreadInterruptedException`를 사용하여 스레드의 실행 흐름을 제어하면, 스레드 간의 상호작용과 실행 순서를 조절하여 복잡한 다중 스레드 프로그래밍을 더욱 안정적으로 관리할 수 있습니다.
3. System.Threading.Tasks.Task
* 비동기 작업
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Sample
{
internal class Program
{
static void Main(string[] args)
{
Action act1 = () =>
{
Thread.Sleep(1000);
Console.WriteLine("Action1");
};
Action act2 = () =>
{
Console.WriteLine("Action2");
};
Task task1 = new Task(act1); //생성자에게 넘겨받은 무명 함수를 비동기로 호출
task1.Start();
Task task2 = new Task(act2);
task2.Start();
task1.Wait(); // task 비동기 호출이 완료될때 까지 기다림.
task2.Wait();
Console.WriteLine("complete");
}
}
}
4.Task<Tresult>
* Task 클래스의 제네릭 버전 : 비동기 작업이 완료 되면 결과값을 반환하는 테스크를 나타냄
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Sample
{
internal class Program
{
static void Main(string[] args)
{
var myTask = Task<List<int>>.Run(() =>
{
Thread.Sleep(1000);
List<int> list = new List<int>();
list.Add(3);
list.Add(4);
list.Add(5);
return list; //Task<Tresult>는 Tresult 형식의 결과를 반환함.
});
var myList = new List<int>();
myList.Add(0);
myList.Add(1);
myList.Add(2);
myTask.Wait();
myList.AddRange(myTask.Result.ToArray());
int index = 0;
foreach (int t in myList)
{
Console.WriteLine($"index{index} : {t}");
index++;
}
}
}
}
5. Async/await [비동기 코드 만들기]
[참고] - 동기 비동기 블로킹 논블로킹
- Async 키워드는 해당 메서드가 비동기 메서드임을 나타내며
- await 키워드는 비동기 작업의 완료를 기다리는 데 사용 됨.
Async 한정자
- 메소드, 이벤트 처리기, 태스크 등을 수식함
- C# 컴파일러가 async한정자로 수식한 호출자를 만나면 호출 결과를 기다리지 않고 바로 다음 코드로 이동하도록 실행 코
드를 생성
- async로 한정하는 메소드는 반환 형식이 Task나 Task<TResult>, void 중 하나여야 함
- async로 한정한 Task 또는 Task를 반환하는 메서드/태스크/람다식은 await 연산자를 만나는 곳에서 호출자에게 제어 반환
- await 연산자가 없는 경우에는 동기로 실행 [ 처리 결과 반환하기까지 작업이 블로킹된다. ]
[참고] 람다식 -> 자바스크립트에서 화살표함수
[코드]
using System;
using System.Threading.Tasks;
namespace Sample
{
internal class Program
{
async static private void MyMethodAsync(int count)
{
Console.WriteLine("task1... C");
Console.WriteLine("task2...... D");
await Task.Run(async () =>
{
for (int i =0; i<=count;i++)
{
Console.WriteLine($"{i}/{count}");
await Task.Delay(100); //Thread.Sleep()과 같은 역할.
}
});
Console.WriteLine("task5... G");
Console.WriteLine("task6... H");
}
static void Caller()
{
Console.WriteLine("A");
Console.WriteLine("B");
MyMethodAsync(3);
Console.WriteLine("E");
Console.WriteLine("F");
}
static void Main(string[] args)
{
Caller();
Console.ReadLine(); //프로그램 종료 방지
/* readline 없으면
A
B
task1... C
task2...... D
E
F
0/3
*/
}
}
}
* Main 함수에서 Console.ReadLine(); 이 없으면 왜 MyMethodAsync 가 끝나기 전에 프로그램이 종료될까?
1)
`MyMethodAsync` 함수는 비동기 메서드로서 `await` 키워드를 사용하여 비동기 작업을 실행합니다.
이 때, `await Task.Run(...)`을 사용하여 새로운 Task를 생성하여 별도의 스레드에서 작업을 수행합니다.
문제는 `MyMethodAsync` 함수가 아래와 같은 구조를 가지고 있기 때문입니다:
```csharp
async static private void MyMethodAsync(int count)
{
// 일부 코드 생략
await Task.Run(async () =>
{
// 일부 코드 생략
});
// 일부 코드 생략
}
```
비동기 메서드가 비동기 작업을 수행할 때, 해당 메서드의 실행은 `await` 키워드를 만나기 전까지만 일시 중지됩니다. `Task.Run(...)`은 새로운 스레드에서 작업을 실행하고, `await` 키워드는 그 스레드가 완료될 때까지 대기합니다. 하지만 `Task.Run(...)`은 완료를 기다리지 않고 바로 다음 코드를 실행합니다.
따라서 `MyMethodAsync` 함수가 `Task.Run(...)`을 만나고 새로운 스레드에서 비동기 작업을 시작하면, 이 스레드는 `await` 키워드를 만나기 전까지 실행을 계속합니다. 그러면 원래의 스레드는 `MyMethodAsync`의 나머지 부분을 빠르게 실행하고, `MyMethodAsync`의 실행이 끝나지 않았더라도 종료됩니다.
결과적으로, `Main()` 함수에서 `MyMethodAsync`를 호출하고 바로 다음 코드를 실행하므로 `MyMethodAsync`가 완료되기 전에 프로그램이 종료되는 것입니다.
2)
비동기 작업이 실행되는 동안 메인 스레드가 먼저 종료되기 때문입니다.
비동기 작업인 Task.Run에 의해 생성된 작업이 백그라운드에서 실행되는데,
메인 스레드가 바로 다음 코드를 실행하는 동안 백그라운드 작업이 완료되지 않습니다.
따라서 Main 메서드의 마지막 줄인 Console.ReadLine();이 실행되기 전에 메인 스레드가 종료되어
콘솔 창이 닫히면서 출력 결과를 확인할 수 없게 됩니다.
비동기 작업을 수행할 때, 메인 스레드가 모든 작업이 완료될 때까지 기다리도록 조치를 취해야 합니다.
이를 위해 비동기 작업이 완료될 때까지 기다리는 Task.Wait 메서드를 사용하거나
비동기 메서드인 async/await를 사용하여 비동기 작업이 완료된 후에 추가 작업을 실행할 수 있도록 합니다.
결과 1 [ await 아예 하지 않은 경우]
A
B
task1... C
task2...... D
task5... G
task6... H
E
F
0/3
1/3
2/3
3/3
결과 2 [await Task.Run 만 한 경우 ]
A
B
task1... C
task2...... D
E
F
0/3 //딜레이없이 3까지 바로 출력
1/3
2/3
3/3
task5... G
task6... H
결과 3 [await Task.Delay(100); 만 한경우]
A
B
task1... C
task2...... D
task5... G
task6... H
E
F
0/3
1/3
2/3
3/3
결과 4 [ await Task.Run + await Task.Delay(100); ]
A
B
task1... C
task2...... D
E
F
0/3 //딜레이발생
1/3
2/3
3/3
task5... G
task6... H
[참고] - Task.Run , Task.Delay
Task.Run
- 해당 작업을 별도의 스레드에서 실행합니다.
- 매개변수로 전달된 작업을 비동기적으로 실행하고, 그 작업을 나타내는 Task 객체를 반환합니다.
- 주로 CPU 집약적인 작업이나 긴 시간이 걸리는 작업을 비동기로 실행하기 위해 사용됩니다.
Task.Delay
- 특정 시간 동안 현재 스레드의 실행을 지연시킵니다.
- 매개변수로 밀리초(ms) 단위의 딜레이 시간을 받으며, 이 시간이 경과한 후에야 다음 코드가 실행됩니다.
- 주로 비동기 프로그래밍에서 일시적인 지연이 필요한 상황에서 사용됩니다.
Winform
- .NET 데스크탑 어플리케이션을 개발할 때 사용할 수 있는 두 가지 UI 라이브러리 중 하나
- 익히기 쉽고 생산성이 뛰어남
[시작]
새 프로젝트 추가 [ ctrl + shift +N ]
폼 디자인 더블클릭 하면 소스코드로 이동.
[사용]
이벤트란
- 사용자의 동작(버튼 클릭, 마우스 이동 등) 또는 시스템의 상태 변화(폼 크기 변경, 타이머 갱신 등)
- 이벤트 발생시 이벤트 처리기( = 메서드)를 호출하도록 구성할 수 있음
1. 마우스 클릭 이벤트 발생시 메세지 박스 팝업.
using System;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.MouseDown += new MouseEventHandler(this.form_func);
}
// Form1이 처음으로 로드될 때 실행되는 이벤트 핸들러
private void Form1_Load(object sender, EventArgs e)
{
// 여기에 폼 로드 시 실행될 코드를 작성할 수 있습니다.
}
private void form_func(object sender , MouseEventArgs e)
{
MessageBox.Show("hiasdsadsadasdasdasdas");
}
}
}