본문 바로가기
C#

1. 기초 문법

by 노오오오오옹 2021. 6. 20.

https://www.csharpstudy.com/CSharp/CSharp-Intro.aspx

 

C# 프로그래밍 언어 - C# 프로그래밍 배우기 (Learn C# Programming)

C# 프로그래밍 언어 C#은 마이크로소프트에서 개발된 객체 지향 프로그래밍 언어로서 Java 나 C++와 비슷한 면들을 많이 가지고 있다. C#을 이야기 하면 자연스럽게 .NET Framework을 함께 이야기 해야

www.csharpstudy.com


1. 시작 구조

namespace StudyC
{
    class Program
    {
        static void Main(string[] args) // Main() 메소드는 static으로 1개만
        {
            System.Console.Write("Hello World!");
        }
    }
}

C# 프로그램은 Main() 시작 함수를 1개만 가지고 있으며, static으로 선언해야한다.

string[] 문자열을 메소드 인자로 받는다.

System.Console : .NET Framework 클래스이며, WriteLine은 화면에 출력하게 한다.


2. 데이터 타입

  • C# 데이터타입
데이터 타입 .NET 데이터 타입 설명
bool System.Boolean True or False
int System.Int32 32비트 singed integer
long System.Int64 64비트 singed integer
uint System.UInt32 32비트 unsinged integer
float System.Single 32비트 double precision 부동 소수점 숫자
double System.Double 64비트 double precision 부동 소수점 숫자
string System.String 유니코드 문자열
  • 리터럴 데이터

C#에서는 123, true, "ABC"와 같은 값을 직접 써줄 수 있는데, 이를 리터럴이라고 한다. 따로 접미어 표시가 없는 경우에도 컴파일러가 할당해준다.

123	// int 리터럴
123.3	// double 리터럴
"A"	// string 리터럴
'A'	// char 리터럴
true	// bool 리터럴

접미어를 표시하면 특정 데이터 타입을 지정할 수 있다. 접미어는 대소문자 구분이 필요없다.

C# 리터럴 데이타 타입 Suffix (대소문자 모두 가능) 예제
long L 1024L
uint U 1024U
ulong UL 1024UL
float F 10.24F
double D 10.24D 또는 10.24
decimal M 10.24M
// Bool
bool b = true;

// Numeric
short sh = -32768;   
int i = 2147483647;  
long l = 1234L;      // L 접미어
float f = 123.45F;   // F 접미어 // F를 붙이지 않으면 double이 디폴트다.
double d1 = 123.45; 
double d2 = 123.45D; // D 접미어
decimal d = 123.45M; // M 접미어

// Char/String
char c = 'A'; // '' 사용
string s = "Hello"; // "" 사용

// DateTime  2011-10-30 12:35
DateTime dt = new DateTime(2011, 10, 30, 12, 35, 0);

※ 각 타입의 최대/최소 값 : 데이터 타입.MinValue or 데이터 타입.MaxValue

  • Null Type

C# 2.0부터 불가능한 타입에도 ?을 통해 NULL로 초기화할 수 있다. ?의 경우 Nullable<T>과 같은 표현이다.

// NULL
int? i = null; // int?는 Nullable<int> 와 같다.
i = 100;

bool? b = null;

Nullable<int> j = null;
j = 10;
int k = j.Value;

3. 변수와 상수

변수는 메소드내 로컬 변수와, 클래스 내 멤버 변수가 있다.

  • 로컬 변수 : 해당 메서드내에서만 사용되며, 호출이 끝나면 소멸된다. 기본값을 할당하지 못하기 때문에 사용전 값을 할당해야한다.
  • 멤버 변수(전역적 변수:필드) : 클래스 객체가 살아있는 한 계속 존속되며, 다른 메서들에서 참조 가능하다.

※ 변수의 이름은 대소문자를 구별한다.

lass Temp
{
    int val; // 필드는 자동으로 초기화해준다.
    const int MAX = 100; // 상수는 첫 할당 이후 값을 변경할 수 없다.
    readonly int MIN; // 생성자에서 값을 할당한다.

    public Temp() // 생성자에서만 할당할 수 있음
    {
        MIN = 10;
    }

    public void Func1()
    {
        int local = 300;

        Console.WriteLine(val);
        Console.WriteLine(local);
        Console.WriteLine(MIN);
    }
}

class Program
{
    static void Main(string[] args)
    {
        Temp temp = new Temp();

        temp.Func1();
    }
}

※ 로컬변수는 반드시 사용전에 할당, readonly는 생성자에서 값을 변경할 수 있다.


4. 배열

동일한 데이터 타입 요소들로 구성된 집합이다. 각 Index를 통해 배열 요소에 접근할 수 있다.

// 1차원 배열
string[] regions = {"서울", "경기", "부산"};

// 2차원 배열
string[,] depts = {{"홍길동", "경리부"}, {"이용", "총무부"}};

// 3차원 배열
string[,,]
  • 가변 배열

각 차원별 배열 요소 크기가 가변적일 경우 [][] 방식을 이용한다.

// 가변 배열로 1차원 배열 크기는 명시해야한다.
int[][] arr = new int[3][];

// 각 1차원 배열에 가변크기의 배열을 추가한다.
arr[0] = new int[2];
arr[1] = new int[3] {1, 2, 3};
arr[2] = new int[5] {10, 20, 30, 40, 50};

// 접근방식
arr[0][0] // 기본값 0
arr[1][1] // 2 
arr[2][3] // 40
  • 배열의 사용방법

배열은 래퍼런스 타입이기 때문에, 전달할 때 데이터를 복사하지 않고 참조 값만 전달한다.

static void Main(string[] args)
{
    int[] scores = { 1, 2, 3, 4, 5 };

    Console.WriteLine(GetSum(scores)); // 베열의 이름을 전달한다.
}

static int GetSum(int[] scores) // 레퍼런스 타입으로 전달함.
{
    int sum = 0;

    for (int i = 0; i < scores.Length; ++i) // Length : 배열 요수의 개수
    {
        sum += scores[i];
    }

    return sum;
}

5. 문자열

C# 문자열은 Immutable로 한번 설정되면 다시 변경할 수 없다.

변경이 되는것 처럼 보이는 이유는 .NET 시스템이 새로운 string 객체를 생성하고 할당하기 때문이다.

static void Main(string[] args)
{
    // 문자열 변수
    string str1 = "Hello";
    string str2 = "World";

    // 문자열 결합
    string str3 = str1 + " " + str2;
    Console.WriteLine("String: {0}", str3);

    // Net 시스템이 새로운 string 객체를 생성하고, New 데이터로 초기화한 후 str3에 다시 할당한다.
    str3 = "New"; // 즉 내부적으로 전혀 다른 메모리를 갖는 객체를 가리킨다.
    Console.WriteLine("String: {0}", str3);
}
  • StringBuilder 클래스

String 클래스는 갱신이 많이 필요한 프로그램에서는 적합하지 않다. 

StringBuilder(Mutable)로 별도의 메모리를 생성/소멸 시키지 않고, 일정한 버퍼로 문자열을 효율적으로 관리하자.

static void Main(string[] args)
{
    StringBuilder sb = new StringBuilder();

    for (int i = 1; i < 9; i++)
    {
        sb.Append(i.ToString());
        //sb.Append(System.Environment.NewLine); // 공백라인 추가하기
    }

    string str = sb.ToString();
    Console.WriteLine("String: {0}", str);
}

6. enum 

열거형 상수를 표현하기 위해 각 상수 숫자들에 의미를 부여해 읽기 쉽게 만든 것

public enum Category
{
    Cake,	// 0
    IceCream,	// 1
    Bread	// 2
}

enum은 순서대로 부여되지만 중간에 바꿀 수 있으며 숫자형 타입과 호환이 가능하다.

enum City
{
    Seoul,  // 0
    Daegu,  // 1
    Busan = 3,  // 3: 값을 정해줄 수 있다.
    Jeju = 15   // 15
}

class Program
{
    static void Main(string[] args)
    {
        City city;

        city = City.Busan;
        int citiValue = (int)city; // value: 3

        Console.WriteLine("{0}", citiValue);
    }
}

※ 필드의 경우 자동으로 1번 enum이 부여되지만, 로컬은 할당하지 않으면 컴파일 에러가 발생한다!

 

  • 플래그 enum

[Flags] 속성으로 비트 필드(1,2,4,8,...)을 부여할 수 있다. 

이후 OR AND 연산자를 이용해 다중값, 특정 멤버를 포함하고 있는지 확인할 수 있따.

[Flags]
enum Border
{
    None = 0,
    Top = 1,
    Bottom = 2,
    Right = 4,
    Left = 8
}

class Program
{
    static void Main(string[] args)
    {
        Border border = Border.Top | Border.Left; // 좌상단

        if (border.HasFlag(Border.Top)) // Top 값을 보유했는가?
        {

        }
    }
}

7. 연산자

종류 연산자 예제
산술 연산자 +, -, *, /, % int a = (x + y - z) * (b / c) % d;
할당 연산자 =, +=, -=, *=, /=, %= sum += a;
증감 연산자 ++, -- i++;
논리 연산자 && (And), || (Or), ! (Not) if ((a > 1 && b < 0) || c == 1 || !d)
관계/비교 연산자 <, >, ==, !=, >=, <= if (a <= b)
비트 연산자 & (AND), | (OR), ^ (XOR) byte a=7;
byte b=(a & 3) | 4; // (111 & 011) | 100 // 011 | 100 // 111 // 7
Shift 연산자 >>, << int i=2;
i = i << 5; // 0000 0010 -> 0100 0000
조건 연산자 ?
?? (C# 3.0 이상만 지원)
int val = (a > b) ? a : b;
string s = str ?? "값"; // str이 null이면 값 문자열을 대입한다.
  • ?? 연산자

?? 왼쪽 피연산자의 값이 null이면 뒤의 피연산자 값을 대입한다.

// int 타입의 null
int? a = null;
int b = a ?? 3; // b=3

// string 타입의 null
string str1 = null;
string str2 = s1 ?? "Hello"; // str2="Hello"

8. 조건문

  • if 조건문
if( ... ) // true or false
{
    ...
}
  • switch case 조건문
switch(num)
{
    case 0:
    case 1: // 0과 1일때 
    	break;
        
    case 2: // 2일때
    	break;
        
    default: // 아무것도 속하지 않을 때
    	break;
}

9. 반복문

  • for 반복문
for(int i=0; i<10; ++)
  • foreach
foreach(int n in arr) // arr 배열속의 arr[i] 값들을 n으로 하나씩 가져옴 
  • for vs foreach

for가 살짝 더 빠르나, foreach 또한 최적화로 인해 좋아졌다. 다중 배열의 경우 더욱 가독성이 높은 장점이 있다.

static void Main(string[] args)
{
    // 3차배열 선언
    string[,,] arr = new string[,,] 
    { 
            { {"1", "2"}, {"11","22"} }, 
            { {"3", "4"}, {"33", "44"} }
    };

    //for 루프 : 3번 루프를 만들어 돌림
    for (int i = 0; i < arr.GetLength(0); i++)
    {
        for (int j = 0; j < arr.GetLength(1); j++)
        {
            for (int k = 0; k < arr.GetLength(2); k++)
            {
                Debug.WriteLine(arr[i, j, k]);
            }
        }
    }

    //foreach 루프 : 한번에 3차배열 모두 처리
    foreach (var s in arr)
    {
        Debug.WriteLine(s);
    }
}
  • whil
while(...) // true면 계속 실행
  • dowhile
do
{
    ...
} while (...) // true면 다시 실행함.

10. yield

호출자에게 컬렉션 데이터를 하나씩 리턴할 때 사용한다. 흔히 Enumerator(반복자)라고 불리기도 한다.

즉 데이터를 호출자에게 1개씩 보내주는 역할이다.

  • yield return : 컬렉션 데이터를 하나씩 리턴
  • yield breakl : 리턴을 중지하고 iteration 루프를 바져나감
class Program
{
    static IEnumerable<int> GetNumber()
    {
        yield return 10; // 1번째 리턴
        yield return 20;
        yield return 30;
    }

    static void Main(string[] args)
    {
        foreach (int num in GetNumber()) // 1~3까지 리턴함
        {
            Console.Write(num + " ");
        }
    }
}
}

그렇다면 언제 쓰이는가?

  • 데이터의 양이 많아 하나씩 리턴하는 것이 효율적일 때
  • 무제한의 데이터를 리턴하는 경우
  • 모든 데이터를 미리 계산하면 느리기 때문에 분산 계산할 때
  •  yield와 Enumerator

IEnumerator 인터페이스는 Current MoveNext(), Reset() 3개의 멤버로 이루어져있다.

그중 Current(반환)와 MoveNext(다음 yield 반환하는 곳으로)는 반드시 구현해야한다.

public class MyList
{
    private int[] data = { 1, 2, 3, 4, 5 };

    public IEnumerator<int> GetEnumerator()
    {
        int i = 0;
        while (i < data.Length)
        {
            yield return data[i]; // 1에서 반환하면, 다음 호출 시 1을 기억하기 때문에 2를 반환함
            i++;
        }
    }
    
    // ... Current와 MoveNext는 yield문을 만날 때, 자동으로 인터페이스의 내부 클래스를 만들어준다.
}

namespace StudyC
{
    class Program
    {
        static void Main(string[] args)
        {
            // foreach를 이용
            var list = new MyList();

            foreach (var item in list)
            {
                Console.WriteLine(item);
            }

            // movenext를 이용
            IEnumerator<int> it = list.GetEnumerator();

            it.MoveNext();
            Console.WriteLine(it.Current);  // 1
            it.MoveNext();
            Console.WriteLine(it.Current);  // 2
        }
    }
}

11. 예외처리

try, catch, finally 라는 키워드를 사용해 Exception을 핸들링한다.

또한, throw 키워드로 예외를 던질 수 있다.

try
{
    Do();
}

// catch { ... } 와 같은 기능
catch(Exception ex) // 모든 예외처리
{
    ...
    
    throw; // 다시 예외를 던짐
}
  • try-catch-finally
  • try : 실행시키고 싶은 프로그램의 명령문이 있는 블록
  • ctach : 모든 Exception 이나 특정 Exception을 선별하여 잡는 곳
  • finally : Exception 발생 여부와 관계없이 마지막에 반드시 실행되는 블록이다.
try
{
    ... // 실행할 문장들
}

catch (ArgumentException ex)
{
    ... // Argument에 관한 예외처리
}

catch (AccessViolationException ex)
{
    ... // AccessViolation에 관한 예외처리
}

fianlly
{
    ... // 마지막에 반드시 실행되는 문장
}
  • throw 

try 블럭에서 발생한 Exception은 이미 catch에서 처리된 것이다.

다시 Exception을 보내고 싶을 때 throw 키워드를 사용한다.

  • throw문 다음 catch에서 전달받은 Exception 객체를 쓰는 경우
throw ex; // 기존 Call Stack 정보를 소실하기 떄문에 사용하지 않는 것을 추천함.
  • throw문 다음에 새 Exception 객체를 생성해 전달하는 경우
throw new MyException(..., ex); // 기존 ex를 전달해 어느 라인에서 에러가 발생한지도 알려주자. 
  • throw문 다음에 아무것도 없는 경우
throw; // catch문에서 잡힌 ex를 그대로 상위 호출 함수로 전달하는 작업이다.

※ 다만 throw문과 동일한 메서드(다른 메서드의 위치는 앎)에서 에러가 발생하면 어느 라인에서 발생한지는 모른다.

try
{
    Step1();
    Step2();
    Step3();
}

catch(IndexOutOfRangeException ex)
{
    throw new MyException("Invalid index", ex); // 새로운 ex 생성
}

catch(FileNotFoundException ex)
{    
    ...
    
    if (...) // 기존 Exception을 throw
    {
        throw ex;
    }
}

catch(Exception ex)
{    
    ...
    
    throw; // ex를 그대로 호출자에게 전달
}

12. 네임스페이스

클래스들을 충돌없이 관리하기 위해 사용하며, 없어도 클래스들을 정의할 수 있지만 보통 사용한다.

namespace My
{
    class A
    {
    	...
    }
    
    class B
    {
    	...
    }
}
  • 참조

다른 네임스페이스를 사용하기 위해서는 클래스명 앞에 네임스페이스 전부를 쓰거나, using 키워드를 이용한다.

// 클래스명 앞에 네임스페이스 기입
static void Main(string[] args)
{
    System.Console.WriteLine(); // 네임 스페이스.클래스.함수();
}

// using 이용
using System;

static void Main(string[] args)
{
    Console.WriteLine(); // 클래스.함수();
}

13. 구조체

  • Value Type, Reference Type

C#에서는 struct는 value type이고, class는 reference type으로 만든다.

이름 상속 여부 데이터 타입 파라미터 전달
struct 불가능 벨류 : 기본 데이터 타입(int, double, float, double) 데이터 Copy
class 가능 레퍼런스 : 보다 복잡한 데이터와 행위 객체(Heap)에 대한 레퍼런스
public struct Int32 { ... } // 벨류
public sealed class String { ... } // 레퍼런스
  • 구조체

클래스와 같이 메소드, 프로퍼티 등 비슷한 구조를 가질 순 있으나 상속은 불가능하다.

대신 인터페이스를 구현할 순 있다.

struct Point
{
    public int x; // default는 private이다.
    public int y;

    public Point(int a, int b)
    {
        this.x = a;
        this.y = b;
    }

    public override string ToString()
    {
        return string.Format("{0},{1}", x, y);
    }
}

14. 클래스

클래스는 메소드, 속성(프로퍼티), 필드, 이벤트 등을 멤버로 포함한다. 

클래스 멤버종류설명

멤버 설명
메소드 대개 동사 혹은 동사+명사 식으로 메서드명을 정함. 예) Calculate(), DeleteData()
속성(프로퍼티) 클래스의 내부 데이터를 외부에서 사용 및 설정한다.
필드 클래스의 내부 데이터는 필드에 저장한다.
데이터는 주로 private 필드로 만들고 public 프로퍼티를 이용한다.
이벤트 객체 내부의 특정 상태를, 어떤 일이 있어났다는 이벤트를 외부로 전달한다.
public class Customer // public이니 모든 객체로 부터 접근 가능
{
    // 필드
    string name;
    int age;

    // 이벤트
    public event EventHandler nameChanged;

    // 생성자
    public Customer()
    {
        name = string.Empty;
        Age = -1;
    }

    // 프로퍼티
    public string Name
    {
        get => this.name;

        set
        {
            if (this.name != value)
            {
                this.name = value;
                
                if (nameChanged != null)
                {
                    nameChanged(this, EventArgs.Empty); // 이벤트!
                }
            }
        }
    }

    public int Age { get => this.age; set => this.age = value; }
}
  • Partial 클래스

하나의 클래스를 2개 이상의 파일로 나누어 정리할 수 있는 기능이다.

public partial class MyClass { ... } // MyClass.cs와 MyClass.desinger.cs 파일이 생성

15. Nullable 타입

정수, 부동자릿수, 구조체 등의 Value Type은 NULL을 가질 수 없다. 다만 Nullable로 할당할 순 있다.

Nullable 타입은 Value 값과 Null 상태를 체크(HaslValue)할 수 있는 기능이 있는 struct 이다.

int? i = null; // Nullable<int> i = null
bool? b = null;
int?[] arr = new int?[100];
  • Nullable<T>

실제 값을 나타내는 Value와 값을 가졌는지 체크하는 HasValue 프로퍼티가 있다.

해당 구조체는 타입 캐스트를 통한 변환, 암묵적 변환으로 Value Type으로 변환해준다. 

double sum = 0;
DateTime time;
bool? bSelect;

public void CheckInput(int? i, double? d, DateTime? time, bool? selected)
{
    if (i.HasValue && d.HasValue) // 값 체크
        this.sum = (double)i.Value + (double)d.Value;

    if (!time.HasValue) // 값 체크
        throw new ArgumentException();
        
    else
        this.time = time.Value;

    this.bSelect = select ?? false; // select가 null이면 false를 할당한다.
}
  • Nullable 정적 클래스

System.Nullable 정적 클래스는 Nullable 객체를 비교하는데 도움이 된다.

void NullableTest()
{
    int? a = null;
    int? b = 0;
    
    int res = Nullable.Compare<int>(a, b) // 큰지 작은지를 비교함.
    // 결과 -1
    
    double? c = 0.01;
    double? c = 0.0100;
    
    bool res2 = Nullable.Equals<double>(c, d); // 값이 값은가?
    // 결과 true
}

16. 메서드 파라미터

// 접근 제한자, 리턴 타입, 함수 명칭(인자 1, 인자 2, ..., 인자 N)
public int GetData( ... );
  • Call By Value

인수를 전달할 때 값을 복사해서 전달하는 방식이다. 리턴 후 값의 변경은 없다.

static void Add(int n) 
{
    n += 2; 
}

static void Main(string[] args)
{
    int val = 10;
    
    Add(val); // val은 계쏙 100
}
  • Call By Reference

C#에서는 레퍼런스 전달을 ref 키워드를 사용하며, 사전에 인자로 올 변수를 초기화해야한다.

또한, out 키워드로도 가능하다. out은 사전에 초기화를 하지 않아도 된다.

static void Main(string[] args)
{
    int x = 1;
    double y = 1.2;
    int a, b;

    // ref 키워드 : 초기화 O
    double res = GetMul(ref x, ref y); // 매개변수로 ref 표시!

    // out 키워드 : 초기화 X
    GetDif(1, 2, out a, out b); // 매개변수로 out 표사!

    Console.WriteLine(string.Format("Res = {0} // X = {1} // Y = {2}", res, x, y));
    Console.WriteLine(string.Format("A = {0} // B = {1}", a, b));
}

static double GetMul(ref int a, ref double b) // 인자에 ref를 표시해야한다.
{
    return ++a * ++b;
}

static void GetDif(int a, int b, out int r1, out int r2) // 인자에 out을 표시해야한다.
{
    r1 = a + b;
    r2 = a - b;
}
  • Named 파라미터 

파라미터의 위치에 따라 순차적으로 넘어가는데, C# 4.0 부터는 파라미터 명을 지정하여 전달할 수 있다.

Func(name : "John", age : 10, score : 90.0);
  • Optional 파라미터 (디폴트 O)

C# 4.0 부터 메소드가 디폴트 값을 보유한다면 생략해도 된다. 디폴트는 파라미터의 맨 마지막에 배치해야한다.

int GetSum(int a, int b, int c=10, int d=20);
  • params

파라미터의 갯수를 가변 크기일 때 parms 키워드를 사용한다.

int Calc(params int[] vals);

17. 이벤트

클래스에서 특정한 이벤트가 일어났음을 외부의 이벤트 가입자들에게 알려주는 것을 의미한다.

클래스 내에서 필드처럼 정의되며, event 키워드를 사용해 표시한다.

  • 이벤트 핸들러 : 이벤트가 발생했을 때 어떤 명령을 실행할지를 지정함
  • '+=' 연산자를 이용해 이벤터 핸들러를 이벤트에 추가한다.
  • '-=' 연사자로 이벤트핸들러를 삭제한다.
class MyButton
{
    public string text;

    public event EventHandler onClicked; // 외부에서 엑세스할 수 있게 public

    public void ButtonClicked() 
    {
        if (onClicked != null) // 외부로 보내기전에 가입된 사용자가 있는지 체크한다.
        {
            onClicked(this, EventArgs.Empty); // 가입자가 있으면 이벤트핸들러 호출
        }
    }

}

public void Run()
{
   MyButton button = new MyButton();
   
   buttton.onClicked += new EventHandler(ButtonClick); // 이벤트 핸들러로 ButtonClick()을 등록 
   buttton.text = "Run";
   
   ...
}

static void ButtonClick(object sender, EventArgs e) // 이벤트핸들러로 사용됨
{
    MessageBox.Show("Button 클릭");
}
  • add와 remove

프로퍼티의 get, set 처럼 이벤트에서는 add(+=), remove(-=)가 있다.

private event EventHandler onClicked;
public event EventHandler OnClicked { add => onClicked += value; remove => onClicked -= value; }

※ 이벤트핸들러를 추가한 클래스에서만 삭제가능하다.


18. 전처리기

컴파일이 시작되기 전에 컴파일러에게 명령을 미리 처리하도록 지시하는 것이다.

  • 지시어는 #으로 시작
  • 한 라인에 1개의 전처리기 명령만 사용
  • 지시어의 끝에는 ;를 붙이지 않는다.
  • 해당 파일 안에서만 효력이 발생된다.
#define
#if
#else
#endif
  • region 지시어

코드 블록을 논리적으로 묶을 때 유용하다. 예를 들어, public만 private 등등...

#region Public Methods
public void Run()
{
    ...
}
#endregion
  • 기타 지시어
  • #undef : #define과 반대로 지정된 심볼을 해제할 때 쓴다
  • #elif : else if
  • #line : 라인 번호를 임의로 변경, 파일명을 임의로 다르게 설정
  • #error : 전처리시 Preprocessing을 중단하고 에러 메시지를 출력
  • #warinig :" 경고 메시지를 출력하지만 Preprocessing은 계속 진행
// #warning 예제 -----------------------------------
#if (!ENTERPRISE_EDITION)
#warning This class should be used in Enterprise Edition
#endif

namespace App1 
{
    class EnterpriseUtility 
    {
    
    }
}

// #error 예제 --------------------------------------
#define STANDARD_EDITION
#define ENTERPRISE_EDITION

#if (STANDARD_EDITION && ENTERPRISE_EDITION)
#error Use either STANDARD or ENTERPRISE edition. 
#endif

namespace App1 
{
    class Class1 
    {
    
    }
  • pragma 지시어

컴파일러 제작업체가 고유하게 자신들의 것을 만들어 사용할 수 있는 지시어다.

즉 어떤 컴파일러에 따라 지원하는것이 달라진다.

'C#' 카테고리의 다른 글

2. 고급 문법  (0) 2021.06.21

댓글