어떤 Programming언어든 거기에 능숙해지기 위해서는 기초를 잘 다져두는 것이 중요합니다. Programming에서 가장 기본적인 것 중 하나는 변수와 상수를 사용하는 것입니다. 고사양 game이나 기업용 Application과 같은 고급 Program에서 조차도 어떤 형태로든 변수를 사용합니다. C#에서도 마찬가지인데 변수와 상수는 아주 기본적인 사항에 해당하므로 이에 대해 알아둘 필요가 있습니다.
1. 변수
변수는 Computer memory상의 위치라고 할 수 있습니다. 이 위치는 Application에서 사용되는 Data를 저장하기 위한 예약된 장소로서 변수는 개발자에 의해 이름(식별자)이 정해지게 되고 여기에 값이 할당됩니다. 그리고 변수에 할당된 식별자는 변수로 할당된 값에 접근하기 위해 사용될 수 있습니다. 이렇게 값으로 접근이 가능해지면 해당 값을 읽을 수 있게 되고 필요한 경우 값을 바꿀 수도 있습니다. 물론 값을 변경하는 경우에도 변수의 이름이 사용됩니다.
2. 변수의 선언과 암시적 유형
C#은 type에 안전한 Programming언어로 분류될 수 있습니다. 이는 변수의 Data 유형이 식별될 수 있음을 의미하는 것이며 변수는 정해진 Data 유형이 아닌 다른 유형의 Data를 저장할 수 없다는 뜻이 됩니다. 만약 변수의 유형과 할당된 값의 유형이 다르면 Compile Error를 일으키게 됩니다. 이것은 변수가 선언되면 다양한 유형의 Data를 저장할 수 있는 느슨한 type Programming언어와는 반대되는 개념입니다.
변수의 type을 식별할 수 있는 데는 2가지 방법이 있는데 하나는 변수나 상수를 선언할 때 type annotation을 사용하는 것으로 변수의 이름 앞에 해당 유형을 붙여주는 방법입니다. 아래 예제를 보면 변수명 i앞에 int라는 Data type이 붙어있는 것을 알 수 있습니다.
int i = 10;
type annotation을 사용할때 값은 변수를 선언한 이후에라도 가능합니다.
int i;
i = 10;
변수를 선언할때선언할 때 type annotation을 사용하는 것 이외에 다른 방법으로 C# Compiler가 변수의 유형을 식별하도록 하는 것이 있습니다. 이러한 방법은 Compiler가 변수를 선언할 때 할당된 값의 유형을 보고 판단하는 것으로 값의 유형이 곧 변수의 유형이 됩니다. Compiler가 변수의 유형을 추론할 수 있도록 하려면 변수의 이름 앞에 var keyword를 사용해야 합니다.
var i = 10;
var s = "abc";
위 구문을 Compile 할 때 i변수에는 정수값 10을 할당하고 있으므로 i의 유형은 int가 되고 s에는 문자열이 할당되어 있으므로 string 유형이 됩니다. 이때 var를 암시적 유형이라고 하는데 이런 경우에는 변수선언 시 반드시 초기값을 할당해야 합니다. 따라서 다음과 같은 Code는 실행될 수 없습니다.
var i;
i = 10;
물론 var를 사용한다고 하더라도 초기값을 할당하고 나면 동일한 유형인 경우 다른 값을 할당하는 것 역시 가능합니다.
var i = 10;
i = 20; //가능한 구문
3. 상수
상수는 변수와 비슷하지만 상수가 선언될 때 미리 값을 가지고 있어야 한다는 차이가 있습니다. 또한 가장 중요한 차이점으로 일단 값이 상수에 할당되면 해당 값은 변경할 수 없습니다.
이러한 특징때문에 상수는 특정값이 Application code 전체를 걸쳐 반복적으로 나타나야 하는 경우 유용하게 사용할 수 있습니다. 즉, 매번 실제 값을 사용하기보다 상수를 사용하면 Code를 읽기에도 쉽고 향후 상수의 값을 변경할 필요가 있을 때 상수에 할당된 값만 바꾸면 되므로 Code를 유지보수 하기에도 훨씬 쉽습니다. 예를 들어 실제값을 사용한 경우 Code안에서 숫자 3을 사용했다면 누군가 Code를 읽을 때 3이라는 숫자가 무엇을 의미하는지 명확하지 않을 수 있지만 3 대신 iterationCount라는 상수를 사용한다면 3이라는 게 무엇을 의미하는지 훨씬 명확하게 의미를 전달할 수 있을 것입니다. 또한 이미 언급하였듯이 해당 값이 사용된 전체 Code에 걸쳐 값을 변경해야 하는 경우 상수에 할당된 값만 바꾸면 되므로 Code를 유지보수하는데도 훨씬 유리하게 작용할 수 있습니다.
상수는 const keyword를 사용해 선언되며 값의 유형과 상수이름, 그리고 할당할 값순으로 아래와 같이 사용됩니다.
const int iterationCount = 3;
위 예제처럼 상수는 선언 시 반드시 값으로 초기화되어야 합니다. 따라서 아래와 같이 값이 없는 상수를 선언하는 경우 Compile오류가 발생합니다.
const int iterationCount;
이제 변수와 상수가 무엇인지를 알았으니 숫자형 Data 유형으로 시작하여 다양한 유형에 대해 살펴볼 수 있습니다.
4. Integer 유형
Integer는 가장 광범위하게 사용되는 Data 유형중 하나입니다. C#에서는 사용하려는 숫자의 크기나 또는 정수의 부호화(양수 또는 음수), 비부호화(양수전용)에 따라 다양한 integer type을 제공하고 있습니다. 모든 integer유형의 변수는 한 가지 공통점을 가지고 있는데 그것은 오로지 정수값을 저장하는 데 사용된다는 것입니다.
아래 표에서는 다양한 C# integer type을 보여주고 있으며 각각 차지하는 Memory의 Byte수와 함께 그만큼 수용가능한 수의 범위를 같이 나타내고 있습니다.
byte | 1 byte | 0~255 |
sbyte | 1 byte | -128~127 |
short | 2 byte | -32,768~32,767 |
ushort | 2 byte | 0~65,535 |
int | 4 byte | -2,147,483,648~2,147,483,647 |
uint | 4 byte | 0~4,294,967,295 |
long | 8 byte | -9,223,372,036,854,775,808~9,223,372,036,854,775,807 |
ulong | 8 byte | 0~18,446,744,073,709,551,615 |
위의 Data 유형중 일부만을 예로 사용해 본다면 다음과 같이 변수를 선언할 수 있을 것입니다.
int i = 10;
byte b = 255;
sbyte sb = 127;
C#의 모든 data 유형은 범위 속성을 가지고 있어서 이를 통해 해당 유형에서 가능한 수용가능한 최소값과 최댓값을 확인할 수 있습니다. 아래 Code에서는 Console을 통해 일부 integer type의 최솟값과 최댓값을 나타내고 있습니다.
Console.WriteLine($"byte Min Value = {byte.MinValue}");
Console.WriteLine($"byte Max Value = {byte.MaxValue}");
Console.WriteLine($"short Min Value = {short.MinValue}");
Console.WriteLine($"short Max Value = {short.MaxValue}");
Console.WriteLine($"int Min Value = {int.MinValue}");
Console.WriteLine($"int Max Value = {int.MaxValue}");
Console.WriteLine($"uint Min Value = {uint.MinValue}");
Console.WriteLine($"uint Max Value = {uint.MaxValue}");
Console.WriteLine($"long Min Value = {long.MinValue}");
Console.WriteLine($"long Max Value = {long.MaxValue}");
위 Code는 다음과 같은 결과를 표시할 것입니다.
byte Min Value = 0 byte Max Value = 255 short Min Value = -32768 short Max Value = 32767 int Min Value = -2147483648 int Max Value = 2147483647 uint Min Value = 0 uint Max Value = 4294967295 long Min Value = -9223372036854775808 long Max Value = 9223372036854775807 |
5. 부동소수점 / 소수
소수점이 없는 integer는 정수를 처리하기에는 좋지만 소수점이후의 숫자가 존재하는 경우에는 정확한 값을 담을 수 없습니다. 이러한 경우 대부분 부동소수점 혹은 배정도 부동 소수점형태로 저장해야 하며 C#에서는 float이나 double type을 사용할 수 있습니다.
아래 표는 이러한 유형에서 지원가능한 수의 점위와 자릿수 정확도를 나타내고 있습니다.
float | 8 byte | +/-1.5 * 10-45 ~ +/-3.4 * 1038 | 6-7 자리 |
double | 16 byte | +/-5.0 * 10-324 ~ +/-1.7 * 10308 | 15-16 자리 |
C#에서 부동소수점 값은 기본적으로 double로 인식되기 때문에 아래와 같이 float변수를 선언할 수는 없습니다.
float f = 123.45;
이런 경우 compiler는 아래와 같은 구문오류를 표시할 것입니다.
Literal of type double cannot be implicitly converted to type 'float'; use an 'F' suffix to create a literal of this type |
위의 오류내용중 literal이라고 하는 단어에 주목하시기 바랍니다. 구문에서 literal의 의미는 C# code상에서 명확하게 입력된 모든 값을 말합니다. 이를 테면 123이나 123.45, -10과 같이 직접 사용된 값을 말하며 문자열으로는 'Hello World'나 'abcdefg'같이 표현될 수 있고, 이렇게 직접적으로 사용된 모든 값이 literal로 인식됩니다.
위 예제에서 Literal값은 float변수 f에 할당된 123.45에 해당합니다. 이 예제에서 부동 소수점 literal값이 double type으로 인식되고 변수 f는 float이 되므로 값과 변수의 type이 일치하지 않기 때문에 값을 할당할 수 없는 것입니다. 따라서 이런 경우 C#에게 실제 literal값은 float type이라는 것을 알려줘야 하며 'F'라는 문자를 접미사로 literal숫자의 끝에 아래와 같이 붙여주는 것으로 문제를 해결할 수 있습니다.
float f = 123.45F;
double의 경우는 기본이 double로 인식되므로 별다른 접미사를 필요로 하지 않으며, 아래와 같이 type을 선언하고 값을 할당할 수 있습니다.
float f = 123.45F;
double d = 123.45;
참고로 float과 double은 loop문 내부에서 순회하는 변수처럼 counting변수로서는 사용할 수 없습니다.
6. Decimal
C# 변수에서 Integer와 float계열 변수는 일부 제한을 가지고 있습니다. Integer는 단지 정수형만 다룰 수 있으므로 만약 Integer에서 소수를 다루게 된다면 소수점 이하 수는 모두 잘리게 됩니다. 또한 float은 소수를 다룰 수 있지만 반올림정확도에 문제가 생길 수 있습니다. 이때 decimal type은 이 문제를 모두 해결할 수 있는 방안을 제공합니다. Decimal 유형은 integer와 float 유형사이에서 중간역할을 하는 것으로 값에서 소수 부분을 저장할 수 있고 동시에 계산처리과정에서 정확성을 제공해 줄 수 있습니다.
Literal값을 decimal로 선언하기 위해서는 'M'문자를 접미사로 사용해야 합니다. 아래 예제에서는 2개의 decimal값을 선언하고 있으며 이 둘을 계산하여 그 결과를 표시하고 있습니다.
using System;
decimal d1 = 123.45M;
decimal d2 = 456.78M;
decimal r = d1 * d2;
Console.WriteLine($"Result : {r}");
7. Boolean
C# Boolean유형은 bool keyword를 사용해 선언되며 true와 false값을 저장합니다. 이러한 특징으로 인해 주로 if나 while구문에서 흐름구조를 제어하는데 많이 사용됩니다.
일부 다른 Programming언어와는 달리 C# Boolean변수는 true와 false값으로만 할당할 수 있으며 동일한 값으로 취급되는 1이나 0으로는 할당할 수 없습니다.
bool b = false;
if (b)
{
Console.WriteLine("b > true");
}
else
{
Console.WriteLine("b > false");
}
대부분의 경우 Program에서는 사람이 읽을 수 있는 데이터를 문자 혹은 단어형태로 저장할 수 있는 방법이 필요하며 이때 문자열과 문자 Data유형이 사용됩니다. 또한 C#에서는 문자열보간이라는 개념을 사용하여 다수의 변수를 통해 하나의 문자열을 완성하기도 하는데 이러한 내용과 관련해서는 string과 char형식에 대해 살펴봐야 합니다.
8. 문자(Character)
문자에 대해서 얘기할 때 우리는 각각의 문자나 숫자를 생각해 볼 수 있습니다. 예를 들어 문자 'a'는 Character형이 될 수 있으며 '1'또한 문자가 될 수 있고(숫자 1과는 다릅니다.) 이러한 문자는 C#에서 char유형으로 저장됩니다. 그리고 char는 오로지 하나의 문자만 저장할 수 있습니다.
char형이 하나의 문자가 포함할 수 있다는 사실은 영문자에만 제한되지 않습니다. 또한 Character는 C#에서 내부적으로 grapheme cluster로서 저장됩니다. grapheme cluster는 둘 이상의 Unicode scalar가 결합하여 단일 식별 가능한 문자를 나타내는 것을 의미합니다.
또한 Character는 loop와 수학적 표현식을 통해 처리될 수 있습니다.
변수나 상수에 문자를 할당하려면 문자주위에 '(Single quote)를 사용하여 아래와 같이 표현합니다.
char c = 'a';
const char cc = 'b';
Unicode인 경우 아래와 같은 방식을 사용할 수도 있습니다.
char c = '\u2726';
9. 특수문자
일반적으로 사용되는 문자 이외에 new line, tab, 종료문자와 같이 소위 확장문자열을 지정하는 경우에도 다양한 특수문자를 사용할 수 있으며 이들 특수문자는 backslash(\) 문자를 접두사로 사용하여 나타냅니다.
예를 들어 new line 특수문자에 대응하는 확장문자는 '\n'이 됩니다. 아래 예제는 new line 특수문자를 사용해 문자열 안에서 빈 줄을 추가하도록 합니다.
Console.WriteLine("New Line\n\nNew Line");
위의 Code를 실행하면 결과는 다음과 같이 표시될 것입니다.
New Line New Line |
따라서 앞에 접두사로'\'문자가 사용된 모든 문자는 특수문자로 인식되며 그에 따라 적절히 처리됩니다. 간혹 '\'문자 자체를 표현해야 하는 경우도 있는데 그런 경우에는 '\\'와 같이 '\'문자를 2개 연속해서 사용합니다. 따라서 아래 예제는
Console.WriteLine("\\abc");
다음과 같은 결과를 표시합니다.
\abc |
C#에서 지원하는 일반적인 특수문자는 아래표를 통해 확인할 수 있습니다.
\e | Escape 문자 |
\n | New Line |
\r | Carriage return |
\t | Tab |
\\ | Backslash |
\" | 쌍따옴표 |
\' | 홀따옴표 |
\unn | 단일 byte Unicode scalar를 나타내며 이때 nn은 Unicode문자를 나타내는 2개의 16진수로 대체됩니다. |
\unnn | 이중 bytee Unicode scalar를 나타내며 이때 nnn은 Unicode문자를 나타내는 4개의 16진수로 대체됩니다. |
\nnnnnnnn | 4 byte Unicode scalar를 나타내며 이때 nnnnnnnn은 Unicode문자를 나타내는 8개의 16진수로 대체됩니다. |
10. 문자열
지금까지는 char변수에서 단일 문자를 저장하는 유형만을 확인해 보았습니다. 이런 유형은 단일 문자나 숫자를 저장하는 데는 유용하지만 전체 단어나 문자를 배열형태로 저장하는 데는 거의 쓸모가 없습니다. C#에서 지원하는 문자열 유형은 문자열의 상수나 변수에 전체 모든 문자를 저장함으로서 이러한 문제를 해결하고 있습니다.
단일 Literal문자열을 문자열 주위에 큰따옴표(")를 사용하여 아래와 같이 표현됩니다.
"abcdefg"
11. 축자 문자열(Verbatim string) literal
일반적인 문자열은 특수문자를 포함할 수 있습니다. 예를 들어 아래와 같은 Code에서는 문자열에 다수의 특수문자가 포함되어 있는 것을 확인할 수 있습니다.
string s = "abc\n\ndef";
여러 줄에 걸친 text를 만들기 위해서는 위의 문자열값에서와 같이 표준 문자열 literal에 '\n'확장문자열을 사용해야 합니다. 만약 그렇지 않고 아래와 같은 code를 통해 여러 줄의 text를 만들려 하면 compile오류가 발생하게 됩니다.
string s = "abc\n\n
def";
이때 문자열 literal은 쓰인 그대로 해석되기 위해 축자 문자열 literal로 선언할 수 있습니다.
축자 문자열은 literal은 확장문자열이 사용되면 해당 확장문자열이 가진 의미를 무시하게 되며 위 오류가 발생하는 예제마저도 있는 그대로 해석하기에 여러 line에 걸린 text생성을 가능하게 합니다. 문자열 literal을 verbatim으로 선언하기 위해서는 '@'문자를 접두사로 사용해야 합니다. 예를 들어 상기 오류가 발생한 예제는 아래와 같이 선언할 수 있으며
string s = @"abc\n\n
def";
Console.WriteLine(s);
이를 실행하면 다음과 같은 결과를 표시할 것입니다.
abc\n\n def |
12. 문자열보간
문자열은 문자열보간을 사용해 다른 문자열변수나 상수, 표현식 및 함수호출등으로 구성될 수 있습니다.
문자열 보간은 '$'문자를 접두사로 선언함으로써 문자열 내부에 필요한 item을 결합하는 방법으로 구현됩니다. 이때 각 item은 변수나 상수이름이 될 수 있고 심지어 호출되는 함수이름이나 계산식과 같은 표현식이 될 수 있는데 이러한 이름을 중괄호({})로 감싸서 표현합니다.
예를 들어 아래 예제의 경우 Console에 문자열을 출력하기 전 문자열 보간을 통해 다양한 source에서 content를 포함시켜 새로운 문자열 변수를 생성하고 있습니다.
string meName = "kim";
int age = 30;
string my = $"Name : {meName}\nAge : {age}";
Console.WriteLine(my);
위 예제를 실행하면 다음과 같은 결과를 표시할 것입니다.
Name : kim Age : 30 |
만약 중괄호 자체를 그대로 사용해야 한다면 아래와 같이 중괄호를 두 번 연속해서 사용해야 합니다.
string my = $"Name : {{meName}}\nAge : {age}";
다만 이런 경우 변수는 치환되지 않고 이름 그대로 표현되므로 주의해야 합니다.
Name : {meName} |
만약 문자열보간과 축자문자열을 같이 사용해야 한다면 $@ 혹은 @$으로 문자열 앞에 접두사를 붙여주면 됩니다.
string my = $@"Name : {meName}
Age : {age}";
따라서 위 예제는 상기 예제와 동일한 결과를 표시할 것입니다.
보간 된 문자열 Literal은 변수로만 할당될 수 있음에 주의해야 합니다. 만약 보간된 문자열을 상수에 할당하려 한다면 Compile오류를 발생시키게 됩니다.
13. 형변환
C# Programming시에는 언어자체에서 그리고 개발자 자신이 직접 생성한 다수의 많은 형식의 Data를 사용하게 됩니다. 따라서 개발자는 필요에 따라 어떤 유형을 사용해야 하는지를 알고 있어야 하므로 암시적형과 명시적 형식의 변환에 대한 개념을 살펴보고 개체의 유형을 구하면서 이와 관련된 내용을 자세히 알아볼 것입니다.
C#언어는 강력한 유형의 언어로서 변수가 특정 유형으로 선언되고 나면 다른 유형으로 바뀔 수 없습니다. 당연히 변수에 할당되는 값역시도 유형이 제한될 수밖에 없습니다. 예를 들어 int형 변수에 문자열값을 할당하는 것은 불가능합니다.
다만 암시적인 혹은 명시적인 형변환을 사용하는 경우와 같은 특정한 상황에는 어떤 특정 유형의 값을 다른 유형의 변수에 할당하는 것이 가능하기도 합니다.
1) 암시형 형변환
Data의 손실 없이 안전하게 사용될 수 있는 경우 C#은 할당연산자를 통해 하나의 유형에서 다른 유형으로 값을 할당할 수 있도록 허용합니다. 예를 들어 long형 변수는 int변수가 담아낼 수 있는 값 그 이상을 저장할 수 있기 때문에 아래와 같은 할당방식은 완벽하게 유효합니다.
int i = 100;
long l = i;
이러한 기법을 암시적 형변환이라고 하는데 C# Code안에서 명시적으로 선언되는 것이 아닌 값의 유형의 의해 int에서 long으로 형변환이 구현되었기 때문입니다.
하지만 반대로 암시적 형변환을 통해 long을 int로 변환하는 것은 불가능합니다. long은 int에 비해 많은 수를 저장할 수 있기 때문입니다.(int는 4byte인 반면 long은 8byte의 크기를 갖습니다.) long을 int변수에 강제로 맞추려 하면 결과적으로 Data손실은 피할 수 없습니다. 이러한 이유로 C# Compiler는 이와 같은 시도가 발생하면 이를 오류로서 표시하게 되고 Code는 Compile 되지 않을 것입니다.
long l = 1381292100921;
int i = l;
따라서 만약 위의 Code를 작성한다면 Compiler는 상기 Code에 대해 아래와 같은 오류를 표시하게 됩니다.
Cannot implicitly convert type 'long' to 'int'. An explicit conversion exists (are you missing a cast?) |
Error내용을 자세히 보면 명시적인 형변환을 수행하는 연산자가 사용되는 경우 형변환이 가능할 수 있음을 나타내고 있습니다.
2) 명시적 형변환
상기 예시와 같이 C# Compiler가 암시적 형변환을 막고 있기는 하지만 개발자 자신은 비록 변수가 long유형이라고 하더라도 int변수가 가질 수 있는 값이상의 값을 가질 수 없다는 것을 인지하고 있을 수 있습니다. 따라서 이 경우 long변수에 저장된 값이 int변수에 할당되는 것은 문제가 되지 않는 상황일 수도 있는데 이런 경우 long값에서 int값으로의 변환을 수행하는 명시적 형변환을 사용할 수 있습니다.
명시적 형변환은 변환하고자 하는 유형을 괄호를 통해 변환하고자 하는 변수의 이름 앞에 지정함으로써 구현할 수 있습니다. 이를 할당연산자라고 하며 아래 예제를 통해 어떤 방식으로 할당연산자를 통해 long값을 int형으로 명시적인 형변환을 수행할 수 있는지 확인할 수 있습니다.
long l = 1234567;
int i = (int)l;
명시적 형변환을 수행하는 경우 변환되는 대상값이 해당변수가 저장할 수 있는 값보다 더 큰 경우 값은 사전 경고 없이 바뀌게 되므로 주의해야 합니다. 예를 들어 아래 예제의 경우 변수 i에 들어가는 값은 int가 수용할 수 있는 최대치의 값으로 바뀌게 됩니다.
long l = 138129210092;
int i = (int)l;
Console.WriteLine($"i value : {i}");
상기 예제를 실행해 보면 아래와 같은 결과를 볼 수 있는데 이를 통해 우리는 i에 할당된 값이 초기에 l변수에 할당된 값과 일치하지 않음을 알 수 있습니다.
i value : 690256620 |
이 경우 변수 l값은 int 유형이 담아낼 수 있는 값의 범위를 넘어선 것으로 결과적으로 변수의 값이 아무런 사전 경고 없이 바뀌게 되었음을 알 수 있습니다. 특히 이러한 방식으로 발생한 Bug는 복잡한 Project일수록 발견하기 매우 어려워질 수 있으므로 주의해야 합니다.
비슷한 사례로 부동 소수점값을 정수형 변수에 할당하는 경우도 있는데 이런 경우에는 특히 본래값이 가지고 있던 소수점값을 완전히 무시하게 됩니다.
또한 이러한 명시적 형변환은 숫자를 다루는 Data유형에만 사용할 수 있습니다. 따라서 string, bool, char 등의 유형에는 형변환을 수행할 수 없습니다.
3) 변수 유형 식별하기
C#에서는 GetType() Method를 통해 변수의 유형을 확인할 수 있습니다. 아래 예제에서는 해당 Method를 통해 변수 i와 변수 s의 유형을 어떻게 식별하고 표시할 수 있는지를 나타내고 있습니다.
string s = "abc";
int i = 100;
Console.WriteLine(s.GetType());
Console.WriteLine(i.GetType());
위 Code를 실행하면 다음과 같은 결과를 표시할 것입니다.
System.String System.Int32 |
'.NET > C#' 카테고리의 다른 글
[C# 13과 .NET 9] VSCode에서 App생성하기 - 5 (0) | 2024.12.05 |
---|---|
[C# 13과 .NET 9] Visual Studio Code 둘러보기 - 4 (0) | 2024.11.27 |
[C# 13과 .NET 9] Visual Studio Code와 .NET9 설치하기 - 3 (1) | 2024.11.26 |
[C# 13과 .NET 9] C# 구조 - 2 (1) | 2024.11.19 |
[C# 13과 .NET 9] C# Programming 언어 - 1 (1) | 2024.11.15 |