공부/C#

식, 연산자, 문장, 조건문, 반복문, null 연산자, 네임스페이스

월러비 2025. 8. 8. 09:09

C# C++ 차이점

  • C# : 완전 객체 지향 언어 → 전역변수, 전역 함수는 없다.
    • static 멤버를 사용하는 것으로 바뀌었다.
  • 시작점이 정해져있다. - 2가지 방법 (2개 중 하나만 써야한다.)
    1. 최상위문
    2. Main함수를 static으로 선언하는것
  • include, 전방선언 다 필요없다.
  • 프로젝트에 정의되어있는 클래스 등을 아무데서나 쓸 수 있다.
  • 헤더파일이 없어서 한 파일 내에서 선언과 정의를 모두 해야한다.
  • C#코드는 실행파일이 아니라 IL 파일이다.
    • OS에서 바로 실행되기 때문이다.
      • 네이티브 언어라고 부른다.
    • 빌드를 플랫폼에 따라 다르게 설정할 필요가 없어진다.
      • 이 실행 환경을 모든 플랫폼이 공유하는 것이다.
    • CRL이라고 부른다.

선택적 매개변수와 명명된 인수

선택적 매개변수(Optional Parameters)

  • 매개변수에 기본값을 지정할 수 있음
  • 호출 시 생략하면 기본값이 사용됨
  • 필수 매개변수가 선택적 매개변수보다 앞에 와야 함
    • 즉, 선택적 매개변수는 뒤에서부터 하나씩 추가해야한다.
    • int a, int b, int c, int d = 0 이런식으로 사용
//int x = 23 -> 선택적 매개변수
void Foo(int x = 23) { Console.WriteLine(x); }

Foo();        // 출력: 23 (기본값 사용)
Foo(42);      // 출력: 42 (명시적 값 사용)

// 여러 개의 선택적 매개변수
void Bar(int a = 0, int b = 0, int c = 0, int d = 0) { ... }
Bar(d:3);     // 특정 매개변수만 지정 가능

  • d:3 → 특정 매개변수만 기본값을 지정해서 사용할 수 있다.

명명된 인수(Named Arguments)

  • 매개변수를 이름으로 지정할 수 있음
  • 순서와 관계없이 인수 전달 가능
  • 코드의 가독성을 높여줌
void Foo(int x, int y) { Console.WriteLine($"{x}, {y}"); }

// 다양한 호출 방법
Foo(x:1, y:2);    // 출력: 1, 2
Foo(y:2, x:1);    // 출력: 1, 2 (순서 변경 가능)

// 위치 기반 인수와 혼합 사용
Foo(1, y:2);      // 출력: 1, 2

  • Foo(y:2, x:1) → 이렇게하면 매개변수의 순서가 아니라 이름으로 지정해서 값이 할당된다.

ref 지역 변수와 반환

ref 지역 변수

  • 배열 요소나 필드를 참조할 수 있는 지역 변수
  • 변수가 가리키는 저장소를 직접 참조함
    • 특정 위치의 값이 변경될 수 있다.
int[] numbers = { 0, 1, 2, 3, 4 };
ref int numRef = ref numbers[2];     // numbers[2] 참조

numRef *= 10;                        // numbers[2]가 변경됨
Console.WriteLine(numRef);           // 출력: 20
Console.WriteLine(numbers[2]);       // 출력: 20

  • ref int numRef = ref numbers[2] → 2번 인덱스를 ref 변수에 참조하려는 것이다.
  • int[] numers2 = numbers; → 가능
  • numbers = null;
    • numers[2] = -1; → 예외가 난다.
    • numbers2[2] = -1; → 예외가 나지 않는다.
    • 이유 : 설 하나의 주소를 바라봤지만 numers는 null로 가리키는 주소를 삭제했다.
  • ref int[] numbers2 = ref numbers; → 이렇게하면 위의 내용에서 2개 모두 에러가 난다.
    • 이유 : ref는 할당하는 변수의 다른 이름이기 때문에 2개는 같은 주소로 가리키고 있는 것이다.

ref 반환

  • 메서드가 변수에 대한 참조를 반환할 수 있음
  • 호출자는 반환된 참조를 통해 원본을 수정할 수 있음
class Program
{
    static string x = "Old Value";

    static ref string GetX()   
    {
		    return ref x; // x에 대한 참조 반환
    }

    static void Main()
    {
        ref string xRef = ref GetX();    // 참조로 받음
        xRef = "New Value";              // 원본 x 수정
        Console.WriteLine(x);            // 출력: New Value
    }
}

암시적 타입 지정과 새로운 표현식

var - 암시적 타입 지정 지역 변수

  • var 키워드를 사용하면 컴파일러가 타입을 추론함
  • 선언과 동시에 초기화해야 함
  • 한번 추론된 타입은 변경할 수 없음
  • 매개변수, 매서드에서 사용할 수 없다.
    • 지역변수에만 사용 가능하다.
var x = "hello";                  // string으로 추론
var y = new StringBuilder();      // StringBuilder로 추론
var z = (float)Math.PI;          // float로 추론

// 컴파일 오류 예제
var x = 5;
x = "hello";    // 오류: x는 int로 추론되었음

// 컴파일러가 타입을 명확히 추론할 수 없는 경우 사용 자제
Random r = new Random();
var x = r.Next();    // x의 타입이 불명확할 수 있음

대상 타입이 지정된 new 표현식

  • C# 9.0부터 도입된 기능임
  • 컴파일러가 타입을 명확히 추론할 수 있는 경우 new 뒤의 타입을 생략할 수 있음
// 기존 방식
StringBuilder sb1 = new StringBuilder();
StringBuilder sb2 = new StringBuilder("Test");
//var sb1 = new StringBuilder(); 이것도 가능하다.

// 대상 타입이 지정된 new 표현식
StringBuilder sb1 = new();
StringBuilder sb2 = new("Test");

// 생성자에서 특히 유용함
class Foo
{
    StringBuilder sb;
    public Foo(string initialValue)
    {
        sb = new(initialValue);    // StringBuilder 타입 추론
    }
}

// 메서드 호출에서도 유용함
MyMethod(new("test"));
void MyMethod(StringBuilder sb) { ... }

식의 개념

  • 식(Expression)은 값을 나타내는 코드 단위임
  • 가장 단순한 식은 상수와 변수임
  • 연산자(Operator)를 사용하여 식을 변환하고 결합할 수 있음
  • 연산자는 하나 이상의 입력 피연산자(Operand)를 받아 새로운 식을 출력함

예제:

// 상수 식
12

// 연산자를 사용한 식
12 * 30

// 복잡한 식 (괄호로 우선순위 지정)
1 + (12 * 30)

식의 종류

기본 식 (Primary Expressions)

  • 언어의 기본적인 구성 요소와 관련된 연산자를 포함한 식임
  • 멤버 접근, 메서드 호출 등을 포함함

예제:

// 멤버 접근 연산자(.)와 메서드 호출 연산자(())를 사용
Math.Log(1)

void 식

  • 값을 반환하지 않는 식임
  • void 식은 다른 식의 피연산자로 사용할 수 없음

예제:

// void 식
Console.WriteLine(1)

// 컴파일 오류 - void 식을 연산에 사용할 수 없음
1 + Console.WriteLine(1)

할당 식 (Assignment Expressions)

  • = 연산자를 사용하여 변수에 값을 할당하는 식임
  • 할당 식도 값을 가지므로 다른 식의 일부가 될 수 있음

예제:

// 단순 할당
x = 2

// 할당의 결과를 사용
y = 5 * (x = 2)  // x에 2를 할당하고 그 값을 사용

// 다중 할당
a = b = c = d = 0  // 모든 변수에 0 할당

  • 복합 할당 연산자는 연산과 할당을 결합한 단축 표현임:
x *= 2   // x = x * 2와 동일
x <<= 1  // x = x << 1과 동일

연산자 우선순위와 결합법칙

연산자 우선순위 (Operator Precedence)

  • 여러 연산자가 사용된 식에서 계산 순서를 결정함
  • 우선순위가 높은 연산자가 먼저 계산됨

예제:

// * 연산자가 + 연산자보다 우선순위가 높음
1 + 2 * 3  // 7 (1 + (2 * 3)와 동일)

연산자의 결합법칙 (Operator Associativity)

왼쪽 결합(Left-associative)

  • 대부분의 이항 연산자는 왼쪽에서 오른쪽으로 계산됨

예제:

// 나눗셈 연산자는 왼쪽 결합
8 / 4 / 2   // 1 ((8 / 4) / 2와 동일)

// 괄호로 계산 순서 변경 가능
8 / (4 / 2) // 4

오른쪽 결합(Right-associative)

  • 할당 연산자(=), 조건 연산자(?:), 람다 연산자(=>)는 오른쪽에서 왼쪽으로 계산됨

예제:

// 할당 연산자는 오른쪽 결합
x = y = 3  // y에 3을 할당한 후 그 값을 x에 할당

연산자 테이블

분류 연산자 기호 연산자 이름 예제 사용자 재정의 가능

기본(Primary)        
  . 멤버 접근 x.y 불가
  ?. Null 조건 x?.y 불가
  () 함수 호출 x() 불가
  [] 배열/인덱스 a[x] 인덱서로 가능
  ++ 후위 증가 x++ 가능
  -- 후위 감소 x-- 가능
단항(Unary)        
  + 양수 값 +x 가능
  - 음수 값 -x 가능
  ! 논리 부정 !x 가능
  ~ 비트 보수 ~x 가능
  ++ 전위 증가 ++x 가능
  -- 전위 감소 --x 가능
승산(Multiplicative)        
  * 곱하기 x * y 가능
  / 나누기 x / y 가능
  % 나머지 x % y 가능
가산(Additive)        
  + 더하기 x + y 가능
  - 빼기 x - y 가능
시프트(Shift)        
  << 왼쪽 시프트 x << 1 가능
  >> 오른쪽 시프트 x >> 1 가능
관계 및 타입(Relational)        
  < 작음 x < y 가능
  > x > y 가능
  <= 작거나 같음 x <= y 가능
  >= 크거나 같음 x >= y 가능
  is 타입 검사 x is y 불가
  as 타입 변환 x as y 불가
동등(Equality)        
  == 같음 x == y 가능
  != 다름 x != y 가능
논리(Logical)        
  & AND x & y 가능
  ^ XOR x ^ y 가능
      OR x
조건부(Conditional)        
  && 조건부 AND x && y & 통해 가능
        조건부 OR
  ?? Null 결합 x ?? y 불가
  ?: 조건 x ? y : z 불가
할당(Assignment)        
  = 할당 x = y 불가
  += 더하고 할당 x += y + 통해 가능
  -= 빼고 할당 x -= y - 통해 가능
  *= 곱하고 할당 x *= y * 통해 가능
  /= 나누고 할당 x /= y / 통해 가능
  %= 나머지 할당 x %= y % 통해 가능
  &= AND 할당 x &= y & 통해 가능
    = OR 할당 x
  ^= XOR 할당 x ^= y ^ 통해 가능
  => 람다 x => x + 1 불가
  • 위 표는 연산자를 우선순위가 높은 것부터 낮은 순으로 나열한 것임
  • 사용자 재정의 가능한 연산자는 연산자 오버로딩을 통해 사용자 정의 타입에 대해 새로운 동작을 정의할 수 있음

예제:

// 연산자 우선순위 예제
int x = 5;
int y = 3;
int z = 2;

// 다양한 연산자 조합
bool result = x + y * z > 10 && x / z == 2 || y % 2 == 1;
// 계산 순서:
// 1. y * z       (승산)
// 2. x + (y * z) (가산)
// 3. x / z       (승산)
// 4. y % 2       (승산)
// 5. > 10        (관계)
// 6. == 2        (동등)
// 7. == 1        (동등)
// 8. &&          (조건부 AND)
// 9. ||          (조건부 OR)
```---

C# 문장(Statement)

문장의 기본 개념

  • 문장(Statement)은 프로그램에서 순차적으로 실행되는 코드의 기본 단위임
  • 모든 문장은 세미콜론(;)으로 끝나야 함
  • 중괄호({})로 여러 문장을 하나의 문장 블록으로 그룹화할 수 있음

선언문(Declaration Statements)

  • 변수나 상수를 선언하는 문장임
  • 같은 타입의 여러 변수를 쉼표로 구분하여 한 번에 선언할 수 있음
// 변수 선언과 초기화
string someWord = "rosebud";
int someNumber = 42;
bool rich = true, famous = false;

// 상수 선언
const double c = 2.99792458E08;
// c += 10;  // 컴파일 오류 - 상수는 변경 불가

지역 변수의 범위(Scope)

  • 지역 변수나 상수의 범위는 선언된 블록 내부로 제한됨
  • 같은 블록이나 중첩된 블록에서 같은 이름의 변수를 선언할 수 없음
int x;
{
    int y;
    int x;  // 오류 - x가 이미 정의됨
}
{
    int y;  // 정상 - y가 범위 내에 없음
}
Console.Write(y);  // 오류 - y가 범위를 벗어남

식문(Expression Statements)

  • 상태를 변경하거나 상태를 변경할 수 있는 것을 호출하는 식으로 구성된 문장임
  • 다음과 같은 형태가 가능함:
    • 할당식(대입식과 증감식 포함)
    • 메서드 호출식(void와 non-void)
    • 객체 생성식
// 변수 선언
string s;
int x, y;
System.Text.StringBuilder sb;

// 식문 예제
x = 1 + 2;               // 할당식
x++;                     // 증가식
y = Math.Max(x, 5);      // 할당식
Console.WriteLine(y);     // 메서드 호출식
sb = new StringBuilder(); // 할당식
new StringBuilder();      // 객체 생성식 (유효하지만 의미 없음)

선택문(Selection Statements)

if 문

  • 조건에 따라 코드 블록의 실행 여부를 결정함
  • else 절을 사용하여 조건이 거짓일 때 실행할 코드를 지정할 수 있음
if (5 < 2 * 3)
    Console.WriteLine("true");  // true 출력

// 코드 블록 사용
if (5 < 2 * 3)
{
    Console.WriteLine("true");
    Console.WriteLine("Let's move on!");
}

// else 절 사용
if (2 + 2 == 5)
    Console.WriteLine("Does not compute");
else
    Console.WriteLine("False");  // False 출력

중괄호와 실행 흐름

  • else 절은 항상 가장 가까운 if 문에 연결됨
  • 중괄호로 실행 흐름을 명시적으로 제어할 수 있음
// 중괄호 없는 경우
if (true)
    if (false)
        Console.WriteLine();
    else
        Console.WriteLine("executes");  // 실행됨

// 중괄호로 실행 흐름 변경
if (true)
{
    if (false)
        Console.WriteLine();
}
else
    Console.WriteLine("does not execute");  // 실행되지 않음

switch 문

  • 변수의 값에 따라 여러 실행 경로 중 하나를 선택함
  • 각 case 절은 명시적인 점프 문장으로 끝나야 함
  • break, goto case x, goto default, return, throw, continue 등을 사용할 수 있음
void ShowCard(int cardNumber)
{
    switch (cardNumber)
    {
        case 13:
            Console.WriteLine("King");
            break;
        case 12:
            Console.WriteLine("Queen");
            break;
        case 11:
            Console.WriteLine("Jack");
            break;
        case -1:  // 조커는 -1
            goto case 12;  // Queen으로 처리
        default:  // 그 외의 경우
            Console.WriteLine(cardNumber);
            break;
    }
}

  • 여러 case를 연속으로 나열하여 같은 코드를 실행할 수 있음:
switch (cardNumber)
{
    case 13:
    case 12:
    case 11:
        Console.WriteLine("Face card");
        break;
    default:
        Console.WriteLine("Plain card");
        break;
}

타입 패턴을 사용한 switch문 (C# 7.0 이상)

  • 타입에 따라 다른 동작을 수행할 수 있음
  • when 절을 사용하여 추가 조건을 지정할 수 있음
void TellMeTheType(object x)
{
    switch (x)
    {
        case int i:
            Console.WriteLine("It's an int!");
            Console.WriteLine($"The square of {i} is {i * i}");
            break;
        case string s:
            Console.WriteLine("It's a string");
            Console.WriteLine($"The length of {s} is {s.Length}");
            break;
        case bool b when b == true:
            Console.WriteLine("True!");
            break;
        case bool b:
            Console.WriteLine("False!");
            break;
        default:
            Console.WriteLine("I don't know what x is");
            break;
    }
}

  • when b == true → b가 true일때 true

switch 식 (C# 8.0 이상)

  • switch를 식으로 사용할 수 있음
  • case 절은 화살표(=>)와 식으로 구성됨
string cardName = cardNumber switch
{
		//12면 Queen이 반환된다.
    13 => "King",
    12 => "Queen",
    11 => "Jack",
    _ => "Pip card"  // default와 동일
};

// 튜플 패턴 사용
int cardNumber = 12;
string suite = "spades";
string cardName = (cardNumber, suite) switch
{
    (13, "spades") => "King of spades",
    (13, "clubs") => "King of clubs",
    _ => "Other card"
};

  • 튜플 패턴은 공부해놓는게 좋다.

반복문(Iteration Statements)

while과 do-while 문

  • while문은 조건이 참인 동안 코드 블록을 반복 실행함
  • do-while문은 코드 블록을 먼저 실행한 후 조건을 검사함
// while 문 예제
int i = 0;
while (i < 3)
{
    Console.Write(i);  // 012 출력
    i++;
}

// do-while 문 예제
i = 0;
do
{
    Console.WriteLine(i);
    i++;
}
while (i < 3);

for 문

  • 초기화, 조건, 반복 작업을 한 구문에서 표현함
  • 각 절은 생략 가능함
// 기본 for문
for (int i = 0; i < 3; i++)
    Console.WriteLine(i);

// 피보나치 수열 계산 예제
for (int i = 0, prevFib = 1, curFib = 1; i < 10; i++)
{
    Console.WriteLine(prevFib);
    int newFib = prevFib + curFib;
    prevFib = curFib;
    curFib = newFib;
}

// 무한 루프
for (;;)
    Console.WriteLine("interrupt me");

foreach 문

  • 열거 가능한 객체의 각 요소에 대해 코드 블록을 실행함
  • 배열이나 컬렉션을 순회할 때 유용함
  • C++의 범위기반 for문과 비슷한 기능이다.
foreach (char c in "beer")
    Console.WriteLine(c);
//string str = "beer"; str[0] -> b

/* 출력:
b
e
e
r
*/

점프문(Jump Statements)

break 문

  • 반복문이나 switch문을 즉시 종료함
int x = 0;
while (true)
{
    if (x++ > 5)
        break;  // 루프 종료
}
// break 후 여기서 실행 계속

continue 문

  • 현재 반복을 건너뛰고 다음 반복을 시작함
// 짝수 건너뛰기
for (int i = 0; i < 10; i++)
{
    if ((i % 2) == 0)
        continue;
    Console.Write(i + " ");  // 1 3 5 7 9 출력
}

return 문

  • 메서드를 종료하고 값을 반환함
  • void가 아닌 메서드는 반드시 값을 반환해야 함
decimal AsPercentage(decimal d)
{
    decimal p = d * 100m;
    return p;  // 호출자에게 값을 반환
}

throw 문

  • 예외를 발생시켜 오류를 알림
if (w == null)
    throw new ArgumentNullException();

Null 연산자 (Null Operators)

소개

  • C#은 널(null) 값을 더 안전하고 효율적으로 다루기 위한 세 가지 전용 연산자를 제공함

널 병합 연산자 (Null-Coalescing Operator)

  • ?? 연산자를 사용함
    • 이항 연산자다.
  • "왼쪽 피연산자가 널이 아니면 그 값을 사용하고, 널이면 오른쪽 값을 사용함" 이라는 의미다.
    • 즉, null이냐 null이 아니냐를 판단하는 연산자다.
// 널 병합 연산자 기본 예제
string s1 = null;
	string s2 = s1 ?? "nothing";   // s1이 null이므로 "nothing" 할당됨
Console.WriteLine(s2);         // 출력: nothing

string s3 = "something";
string s4 = s3 ?? "nothing";   // s3가 null이 아니므로 "something" 할당됨
Console.WriteLine(s4);         // 출력: something

  • 오른쪽 식은 왼쪽이 널이 아닐 경우 평가되지 않음
  • 널 허용 값 타입(nullable value types)에도 사용 가능함

널 병합 할당 연산자 (Null-Coalescing Assignment Operator)

  • C# 8.0부터 도입된 ??= 연산자를 사용함
  • "왼쪽 피연산자가 널이면 오른쪽 값을 할당함" 이라는 의미임
// 널 병합 할당 연산자 예제
string str = null;
str ??= "default value";      // str이 null이므로 "default value" 할당됨
Console.WriteLine(str);       // 출력: default value

str ??= "new value";         // str이 null이 아니므로 할당되지 않음
Console.WriteLine(str);      // 출력: default value

  • 다음 코드와 동일한 동작을 함:
if (str == null)
    str = "default value";

널 조건 연산자 (Null-Conditional Operator)

  • ?. 연산자("엘비스 연산자"라고도 함)를 사용함
  • 널 검사와 멤버 접근을 한번에 수행함
  • 연산자 왼쪽이 널이면 널을 반환하고, 널이 아니면 오른쪽 멤버를 접근함
  • null일수 있지만 호출은 해야하는 상황일때 사용한다.
// 널 조건 연산자 예제
System.Text.StringBuilder sb = null;
string s = sb?.ToString();   // sb가 null이므로 s는 null이 됨
Console.WriteLine(s);        // 출력: (아무것도 출력되지 않음)

sb = new System.Text.StringBuilder("test");
s = sb?.ToString();         // sb가 null이 아니므로 ToString() 호출됨
Console.WriteLine(s);       // 출력: test

  • 인덱서에도 사용 가능함:
string foo = null;
char? c = foo?[1];         // foo가 null이므로 c는 null

  • 체인(chain) 형태로 사용 가능함:
System.Text.StringBuilder sb = null;
string s = sb?.ToString()?.ToUpper();  // sb가 null이므로 s는 null

  • void 메서드 호출에도 사용 가능함:
StringBuilder sb = null;
sb?.Append("test");        // sb가 null이면 아무 동작도 하지 않음

  • 널 조건 연산자와 널 병합 연산자를 함께 사용하면 유용함:
System.Text.StringBuilder sb = null;
string s = sb?.ToString() ?? "nothing";  // 출력: nothing

네임스페이스의 개념

  • 네임스페이스(Namespace)는 타입 이름의 범위를 정의하는 영역임
  • 타입들을 계층적으로 구성하여 충돌을 방지하고 쉽게 찾을 수 있게 함
  • 예를 들어, RSA 암호화를 처리하는 타입은 다음 네임스페이스에 정의되어 있음:
  • C++과 다른점 : C++은 네임스페이스::멤버 이렇게 사용했지만, C#은 접근연산자(.)만 사용한다.
System.Security.Cryptography

  • 네임스페이스는 타입 이름의 일부로 사용됨:
System.Security.Cryptography.RSA rsa =
    System.Security.Cryptography.RSA.Create();

네임스페이스 정의

  • namespace 키워드로 타입의 네임스페이스를 정의함:
namespace Outer.Middle.Inner
{
    class Class1 {}
    class Class2 {}
}

  • 점(.)으로 구분된 이름은 중첩된 네임스페이스를 나타냄:
namespace Outer
{
    namespace Middle
    {
        namespace Inner
        {
            class Class1 {}
            class Class2 {}
        }
    }
}

  • 특정 타입을 참조할 때는 전체 네임스페이스 경로를 포함한 정규화된 이름(fully qualified name)을 사용함
  • Outer.Middle.Inner.Class1과 같이 표현함

using 지시문

기본 using

  • using 지시문으로 네임스페이스를 임포트하면 짧은 이름을 사용할 수 있음:
  • 파일 단위로 사용
using Outer.Middle.Inner;

Class1 c;  // 전체 경로 없이 사용 가능

전역 using (C# 10)

  • global using으로 선언하면 모든 파일에 적용됨:
  • 전역적으로 사용
global using System;
global using System.Collections.Generic;

  • 프로젝트 파일에서 ImplicitUsings가 true면 다음 네임스페이스들이 자동으로 임포트됨:
    • System
    • System.Collections.Generic
    • System.IO
    • System.Linq
    • System.Net.Http
    • System.Threading
    • System.Threading.Tasks

using static

  • using static으로 특정 타입의 정적 멤버를 직접 사용할 수 있음:
  • 코드를 읽기 힘들어지니 사용하지 않는게 좋다.
using static System.Console;

WriteLine("Hello");  // Console.WriteLine() 대신 사용 가능

네임스페이스 내의 규칙

이름 범위

  • 외부 네임스페이스의 이름은 내부 네임스페이스에서 그대로 사용 가능함:
namespace Outer
{
    class Class1 {}
    namespace Inner
    {
        class Class2 : Class1 {}  // Class1을 바로 사용 가능
    }
}

이름 숨김

  • 내부 네임스페이스에 같은 이름이 있으면 내부의 이름이 우선함:
namespace Outer
{
    class Foo {}
    namespace Inner
    {
        class Foo {}        // 이 Foo가 우선함
        class Test
        {
            Foo f1;         // Inner.Foo를 가리킴
            Outer.Foo f2;   // Outer.Foo를 명시적으로 지정
        }
    }
}

네임스페이스와 타입 별칭

타입 별칭

  • 타입 이름 충돌을 피하기 위해 별칭을 사용할 수 있음:
  • C++의 typedef와 같은 기능이다.
    • 별명을 만드는것과 같다.
using PropertyInfo2 = System.Reflection.PropertyInfo;
class Program { PropertyInfo2 p; }

네임스페이스 별칭

  • 전체 네임스페이스에 대한 별칭도 가능함:
using R = System.Reflection;
class Program { R.PropertyInfo p; }

네임스페이스 별칭 한정자

  • global 키워드나 별칭으로 모호성을 해결할 수 있음: