클래스
클래스의 기본 개념
- 클래스(Class)는 C#에서 가장 기본적인 참조 타입임
- 클래스는 데이터와 해당 데이터를 처리하는 기능을 하나의 단위로 캡슐화함
- 현실 세계의 객체나 개념을 프로그램으로 표현할 때 사용함
- C++에서처럼 :: 연산자를 안쓰고 멤버 접근 연산자만으로 접근이 가능하다.
가장 간단한 클래스 선언
class YourClassName
{
}
- 위 코드는 가장 단순한 형태의 클래스 선언임
- 실제로는 이보다 더 많은 멤버들을 포함하여 유용한 기능을 구현함
- internal 접근지정자 : 프로젝트간에서 외부 내부 프로젝트로 나눠서 접근권한을 주는 것이다.
- public이긴 하지만 자신의 ‘어셈블리’ 안에서만 접근이 가능하도록 한 접근 지정자다.
클래스의 구성요소
- 클래스는 다음과 같은 멤버들을 포함할 수 있음:
- 필드(Field): 데이터를 저장하는 변수
- 메서드(Method): 기능을 수행하는 코드 블록
- 프로퍼티(Property): 필드에 접근하는 방법 정의
- 생성자(Constructor): 객체 초기화 담당
- 이벤트(Event): 객체의 상태 변화를 알림
- 인덱서(Indexer): 배열과 같은 인덱스 접근 제공
- 연산자(Operator): 연산자 동작 정의
- 연산자 오버로딩을 의미한다.
- 중첩 타입(Nested Type): 클래스 내부에 정의된 타입
- 클래스 안에 클래스를 정의한것을 의미한다.
- 예를 들어 게임의 캐릭터를 표현하는 클래스는 다음과 같이 구성할 수 있음:
class Character
{
// 필드 - 캐릭터의 상태 저장
string name;
int health;
int level;
// 메서드 - 캐릭터의 행동 정의
void Attack() { }
void TakeDamage(int damage) { }
void LevelUp() { }
}
필드(Fields)
- 필드는 클래스나 구조체의 멤버 변수임
- 객체의 상태를 저장하는 데 사용됨
- 게임 캐릭터의 체력이나 은행 계좌의 잔액과 같은 데이터를 표현할 때 사용함
- 멤버 하나하나마다 접근권한을 줘야한다.
- 기본값은 private다.
- C#은 지역변수를 제외하고 기본값이 정해져있어서 쓰레기값이 들어가지 않는다.
class Octopus
{
string name; // private 필드 - 클래스 내부에서만 접근 가능
public int Age = 10; // public 필드 - 어디서든 접근 가능
}
필드 수식자(Field Modifiers)
- 다음과 같은 수식자 사용 가능:
- static: 정적 필드. 클래스의 모든 인스턴스가 공유하는 값
- public, internal, private, protected: 접근 수식자로 필드의 접근 범위 지정
- readonly: 읽기 전용 필드. 생성자에서만 값 할당 가능
- const와의 차이점 : const는 컴파일 타임때 정해진다.
- 런타임때 할당할 수 없다.
- readonly는 런타임때 할당되고 그 다음부터 변경이 안된다.
- const와의 차이점 : const는 컴파일 타임때 정해진다.
- new: 상속받은 멤버를 숨기는 데 사용
- 필드 수식자와 키워드의 기능이 다르다.
- 키워드 new : 객체 동적 생성
class BankAccount
{
// 모든 계좌에서 공유하는 정적 필드
static decimal interestRate;
// 각 계좌마다 고유한 인스턴스 필드
readonly string accountNumber;
decimal balance;
public BankAccount(string number)
{
accountNumber = number; // readonly 필드는 생성자에서 초기화
}
}
필드 초기화
- 필드는 선언과 동시에 초기화할 수 있음
- 초기화하지 않은 필드는 해당 타입의 기본값을 가짐(숫자는 0, 참조 타입은 null)
- 필드 초기화는 생성자보다 먼저 실행됨
class Example
{
// 직접 초기화
public int Count = 10;
// 메서드 호출 결과로 초기화
static readonly string TempFolder = System.IO.Path.GetTempPath();
// 여러 필드 한번에 선언과 초기화
static readonly int legs = 8,
eyes = 2;
}
상수(Constants)
- 상수는 컴파일 시점에 값이 결정되는 불변값임
- const 키워드로 선언함
- bool, char, string, 숫자 타입, enum 타입만 가능함
- 프로그램에서 변경되지 않는 값을 표현할 때 사용함(예: 원주율, 최대값 등)
- 상수는 인스턴스처럼 쓸 수 있다
public class Test
{
// 프로그램 전체에서 사용할 상수값 정의
public const string Message = "Hello World";
public const int MaxRetries = 3;
public const double PI = 3.141592653589793;
}
상수 vs 읽기 전용 필드
- 상수(const)와 읽기 전용 필드(readonly)는 비슷해 보이지만 중요한 차이가 있음:
- 값이 결정되는 시점
- 상수: 컴파일 시점에 값이 결정됨
- readonly: 실행 시점(런타임)에 값이 결정될 수 있음
- 사용해야 할 경우
- 상수: 절대 변하지 않는 값(예: 원주율)
- readonly: 객체마다 다른 값을 가질 수 있는 경우(예: 생성 시간)
public class Example
{
// 컴파일 시점에 값 결정
public const decimal Version = 2.3M;
// 실행 시점에 값 결정
public static readonly DateTime StartupTime = DateTime.Now;
}
지역 상수
- 메서드 내부에서도 상수를 선언할 수 있음
void Test()
{
const double twoPI = 2 * System.Math.PI;
}
메서드(Method)
- 메서드는 입력 데이터(매개변수)를 받아 처리하고 결과를 반환하는 코드 블록임.
- 메서드 원형(Signature)은 이름과 매개변수 타입의 순서로 구성됨.
메서드에 사용 가능한 모디파이어
분류 모디파이어 설명
| 정적 | static | 인스턴스 없이 클래스에서 직접 호출 가능 |
| 접근성 | public, internal, private, protected | 메서드의 접근 범위 지정 |
| 상속 | new, virtual, abstract, override, sealed | 상속 관련 동작 지정 |
| 부분 메서드 | partial | 부분 클래스의 메서드 정의 |
식 본문 메서드(Expression-bodied Method)
- 단일 식으로 구성된 메서드는 람다 연산자(=>)를 사용하여 간단히 표현할 수 있음:
- 메서드의 ‘본문’을 ‘식’으로 쓰는 것이다.
- 단순하게 return문만 있는 경우에 이렇게 한줄로 쓸 수 있다.
// 일반적인 메서드
int Foo(int x) { return x * 2; } //문 형태
// 식 본문 메서드
int Foo(int x) => x * 2; //식 형태
로컬 메서드(Local Method)
- 메서드 내부에 정의된 메서드임.
- 바깥 메서드의 변수와 매개변수에 접근 가능함.
- 즉, 로컬 메서드가 선언된 메서드에서 선언된 변수와 매개변수에 접근 가능해진다.
- 오버로딩이 불가능함.
- static 키워드를 사용하면 바깥 메서드의 변수 접근을 막을 수 있음. / Instance 멤버에 접근 못함
- 로컬 메서드가 선언된 메서드 안에서만 접근할 수 있다.
void WriteCubes()
{
int baseValue = 10;
Console.WriteLine(Cube(3)); // 27
// 로컬 메서드 - baseValue 접근 가능
int Cube(int value) => value * value * value + baseValue;
// static 로컬 메서드 - baseValue 접근 불가
static int StaticCube(int value) => value * value * value;
}
최상위문에서의 메서드
- 최상위문에서 선언된 메서드는 로컬 메서드로 취급됨:
// 최상위문
int x = 3;
PrintValue(); // 3 출력
// 최상위문의 메서드 - x 변수 접근 가능
void PrintValue() => Console.WriteLine(x);
// 오버로딩 시도 - 컴파일 오류
void PrintValue(int y) => Console.WriteLine(y); // 오류!
인스턴스 생성자(Instance Constructor)
- 생성자는 객체 초기화를 담당하는 특별한 메서드임.
- 생성자를 작성하지 않아도 자동으로 기본 생성자가 생성된다.
암시적 매개변수 없는 생성자 (기본생성자)
- 생성자를 정의하지 않으면 컴파일러가 자동으로 public 매개변수 없는 생성자를 생성함.
- 하나라도 생성자를 정의하면 자동 생성되지 않음:
public class Player
{
// 자동 생성된 생성자 사용 가능
}
var p = new Player(); // OK
public class Player2
{
public Player2(string name) { } // 생성자 정의
}
var p2 = new Player2(); // 컴파일 오류!
생성자와 필드 초기화 순서
- 필드 초기화는 생성자 실행 전에 선언 순서대로 수행됨:
public class Player
{
int shields = 50; // 첫 번째로 초기화
int health = 100; // 두 번째로 초기화
public Player()
{
// 필드 초기화 후 생성자 실행
Console.WriteLine($"Health: {health}, Shields: {shields}");
shields = 500;
health = 200;
Console.WriteLine($"Health: {health}, Shields: {shields}");
}
}
비공개 생성자
- private 생성자로 인스턴스 생성을 제어할 수 있음:
public class Class1
{
private Class1() { } // 비공개 생성자
// 정적 팩토리 메서드로 인스턴스 생성
public static Class1 Create()
{
// 특별한 로직 수행 후 인스턴스 반환
return new Class1();
}
}
// 사용 예
var obj = Class1.Create(); // OK
var obj2 = new Class1(); // 컴파일 오류!
분해자 (Deconstructor)
- 객체의 상태를 여러 변수로 분해할 수 있음.
- 변수 타입을 생략하거나 특정 값을 무시할 수 있음:
- 클래스와 객체가 있을때 객체의 멤버의 값을 가져오고싶을때 사용한다.
public class Rectangle
{
public readonly float Width, Height;
public Rectangle(float width, float height)
{
Width = width;
Height = height;
}
public void Deconstruct(out float width, out float height)
{
width = Width;
height = Height;
}
}
// 다양한 해체 방법
var rect = new Rectangle(3, 4);
// 명시적 타입
float width, height;
rect.Deconstruct(out width, out height);
// var 사용
var (w, h) = rect;
// 특정 값 무시
var (_, h2) = rect;
// 기존 변수 사용
float existingWidth = 0, existingHeight = 0;
(existingWidth, existingHeight) = rect;
객체 이니셜라이저(Object Initializer)
객체 이니셜라이저 vs 선택적 매개변수
- 두 가지 방식으로 객체를 초기화할 수 있음:
// 방식 1: 객체 이니셜라이저
public class Bunny
{
public string Name;
public bool LikesCarrots;
public bool LikesHumans;
}
var b1 = new Bunny
{
Name = "Bo",
LikesCarrots = true
};
// 방식 2: 선택적 매개변수
public class Bunny2
{
public string Name { get; }
public bool LikesCarrots { get; }
public bool LikesHumans { get; }
public Bunny2(
string name,
bool likesCarrots = false,
bool likesHumans = false)
{
Name = name;
LikesCarrots = likesCarrots;
LikesHumans = likesHumans;
}
}
var b2 = new Bunny2("Bo", likesCarrots: true);
- 선택적 매개변수의 장점:
- 읽기 전용 속성을 사용할 수 있음
- 생성자에서 매개변수 유효성 검사 가능
- 선택적 매개변수의 단점:
- 이진 호환성 문제가 있음 (새 매개변수 추가 시 다시 컴파일 필요)
- 비파괴적 변경(non-destructive mutation)이 어려움
- 정리
- 선택적 매개변수 + 읽기 전용 프로퍼티(get) : 불변 객체를 만들고 싶을 때
- 객체 이니셜라이저 : 값을 유연하게 바꾸고 싶거나, JSON / 데이터 매핑처럼 여러 값을 한꺼번에 세팅할 떄
this 참조
- this 키워드는 현재 인스턴스를 참조함.
- 주로 필드와 매개변수의 이름이 같을 때 구분하는데 사용됨:
public class Test
{
string name;
public Test(string name)
{
this.name = name; // this로 필드 구분
}
}
- 인스턴스 메서드에서 다른 메서드를 호출할 때도 사용 가능:
public class Panda
{
public Panda Mate;
public void Marry(Panda partner)
{
Mate = partner;
partner.Mate = this; // 현재 인스턴스를 파트너의 메이트로 지정
}
}
프로퍼티(Properties)
- C#의 멤버 요소 중 하나다.
프로퍼티의 개념과 필드와의 차이점
- 프로퍼티는 필드처럼 보이지만 내부적으로는 메서드와 같이 동작하는 멤버임
- C++의 getter와 setter의 역할을 수행한다.
- 프로퍼티를 통해 필드에 대한 접근을 제어하고 유효성 검사 등의 로직을 추가할 수 있음
- 프로퍼티는 캡슐화(encapsulation)를 구현하는 주요 방법임
프로퍼티의 기본 구문
- get/set 접근자(accessor)를 사용하여 값을 읽고 쓰는 동작을 정의함
- get 접근자는 프로퍼티의 값을 반환함
- set 접근자는 value라는 암시적 매개변수를 통해 새 값을 받음
- value : set구문 안에서 키워드로 쓰인다.
- 프로퍼티의 데이터형을 가진다.
- 외부에서 프로퍼티에 할당한 값을 복사해서 가져온다.
- 약속되어있는 문법이기에 value 이외의 것을 작성하지 못한다.
- 메모리는 필드와 멤버 변수만 만들어지기에 프로퍼티의 메모리는 만들어지지 않는다.
using System;
// 프로퍼티 사용 예제
var msft = new Stock();
msft.CurrentPrice = 30; // set 접근자 호출
msft.CurrentPrice -= 3; // get과 set 접근자 호출
Console.WriteLine(msft.CurrentPrice); // 출력: 27
// Stock 클래스 정의
public class Stock
{
decimal currentPrice; // 비공개 "백킹" 필드
public decimal CurrentPrice // 공개 프로퍼티
{
get { return currentPrice; } //읽을 때 사용
set { currentPrice = value; } //값을 할당할때 사용
}
}
읽기 전용 및 계산된 프로퍼티
- get 접근자만 정의하면 읽기 전용 프로퍼티가 됨
- 다른 데이터로부터 값을 계산할 수 있음
using System;
// 계산된 프로퍼티 사용 예제
var stock = new Stock { CurrentPrice = 30, SharesOwned = 100 };
Console.WriteLine(stock.Worth); // 출력: 3000
public class Stock
{
public decimal CurrentPrice { get; set; }
public decimal SharesOwned { get; set; }
// 계산된 읽기 전용 프로퍼티
public decimal Worth
{
get { return CurrentPrice * SharesOwned; }
}
}
- public decimal CurrentPrice { get; set; } → 단순하게 get과 set을 한다면 이렇게 간단하게 선언할 수 있다.
- 다만, 이렇게하면 단순한 할당 말고 다른 작업을 할 수 없다.
- 보통 public으로 선언하지않고 auto property로 쓰다가 확장이 필요할때 이렇게 사용한다.
자동 구현 프로퍼티
- 간단한 프로퍼티는 자동 구현 프로퍼티(auto-implemented property) 구문을 사용할 수 있음
- 컴파일러가 자동으로 비공개 백킹 필드를 생성함
- 안보이는 필드가 선언되어있다고 생각해라
- 자동 프로퍼티는 메모리가 생성된다.
- 이유 : Symbol에 값을 할당했기 때문이다.
using System;
// 자동 구현 프로퍼티 사용 예제
var stock = new Stock();
stock.Symbol = "MSFT";
Console.WriteLine(stock.Symbol); // 출력: MSFT
public class Stock
{
// 자동 구현 프로퍼티
public string Symbol { get; set; }
public decimal CurrentPrice { get; set; }
}
프로퍼티 초기화
- 자동 구현 프로퍼티는 필드처럼 초기화할 수 있음
using System;
var stock = new Stock();
Console.WriteLine(stock.MaxPrice); // 출력: 999
public class Stock
{
// 초기값을 가진 자동 구현 프로퍼티
public decimal MaxPrice { get; set; } = 999;
}
get/set 접근자의 접근성
- get과 set 접근자는 서로 다른 접근 수준을 가질 수 있음
- 일반적으로 set 접근자의 접근성을 더 제한하는 데 사용됨
using System;
var foo = new Foo();
Console.WriteLine(foo.X); // 읽기 가능
// foo.X = 100; // 컴파일 오류: set은 private임
public class Foo
{
private decimal x;
public decimal X
{
get { return x; } //프로퍼티 선언을 따라간다 (public)
private set { x = Math.Round(value, 2); }
}
}
- get set 접근권한을 쓰지 않으면 프로퍼티 선언을 따라간다.
- 지금은 public
init 접근자 - 유니티 불가능
- C# 9.0부터 도입된 init 접근자는 객체 초기화 시에만 값을 설정할 수 있게 함
- 이후에는 값을 변경할 수 없어 불변성(immutability)을 보장함
using System;
// init 접근자 사용 예제
var note = new Note { Pitch = 50 };
Console.WriteLine(note.Pitch); // 출력: 50
// note.Pitch = 51; // 컴파일 오류: init-only 프로퍼티는 초기화 후 변경 불가
public class Note
{
public int Pitch { get; init; } = 20;
public int Duration { get; init; } = 100;
}
식 본문 프로퍼티 (Expression-bodied properties)
- 람다 식 형태로 프로퍼티를 간단히 정의할 수 있음
- => 연산자를 사용하여 표현함
- get만 받는 읽기전용 프로퍼티가 된다.
using System;
var stock = new Stock { CurrentPrice = 30, SharesOwned = 100 };
Console.WriteLine(stock.Worth); // 출력: 3000
public class Stock
{
public decimal CurrentPrice { get; set; }
public decimal SharesOwned { get; set; }
// 식 본문 프로퍼티
public decimal Worth => CurrentPrice * SharesOwned;
//식 본문 메서드
//public decimal Worth() => CurrentPrice * SharesOwned;
}
인덱서(Indexers)
- [] 인덱스 연산자가 재구현된것을 ‘인덱서’라고 부른다.
- 반환하는 데이터형은 재구현하는 데이터형에 따라 바꿀 수 있다.
인덱서의 개념
- 인덱서는 클래스나 구조체가 배열처럼 인덱스로 접근할 수 있게 해주는 멤버임
- C# 문자열은 인덱서를 사용하여 개별 문자에 접근할 수 있음:
using System;
string s = "hello";
Console.WriteLine(s[0]); // 'h' 출력
Console.WriteLine(s[3]); // 'l' 출력
인덱서 구현
- 인덱서는 this 키워드를 사용하여 정의함
- 배열의 인덱스처럼 대괄호([])를 사용하여 접근함
- get과 set 접근자를 포함할 수 있음
다음은 문자열 배열을 포함하는 클래스에서 인덱서를 구현한 예제임:
using System;
// 먼저 객체 생성과 사용
Sentence s = new Sentence();
Console.WriteLine(s[3]); // "fox" 출력
s[3] = "kangaroo"; // 값 변경
Console.WriteLine(s[3]); // "kangaroo" 출력
// 그 다음 클래스 정의
class Sentence
{
string[] words = "The quick brown fox".Split();
// 인덱서 정의
public string this[int wordNum]
{
get { return words[wordNum]; }
set { words[wordNum] = value; }
}
}
다중 매개변수 인덱서
- 하나의 타입이 여러 인덱서를 가질 수 있음
- 각 인덱서는 서로 다른 매개변수 타입을 가질 수 있음
- 하나의 인덱서가 여러 매개변수를 가질 수도 있음
using System;
// 먼저 사용 예제
var obj = new MultiIndexer();
string value = obj[1, "test"]; // 여러 매개변수로 접근
// 그 다음 클래스 정의
class MultiIndexer
{
// 여러 매개변수를 가진 인덱서
public string this[int arg1, string arg2]
{
get { return $"Called with {arg1} and {arg2}"; }
set { /* 설정 로직 */ }
}
}
읽기 전용 인덱서와 식 본문 멤버
- set 접근자를 생략하면 읽기 전용 인덱서가 됨
- C#의 식 본문 멤버 문법으로 간단히 표현 가능함
using System;
// 먼저 사용 예제
ReadOnlySentence s = new ReadOnlySentence();
Console.WriteLine(s[0]); // "The" 출력
// 그 다음 클래스 정의
class ReadOnlySentence
{
string[] words = "The quick brown fox".Split();
// 읽기 전용 인덱서를 식 본문으로 정의
public string this[int wordNum] => words[wordNum]; //get
}
인덱스와 범위(Index and Range) 지원 - 유니티할때 안쓴다.
- System.Index와 System.Range 타입의 매개변수를 받는 인덱서를 정의할 수 있음
- ^ 연산자나 .. 연산자로 배열의 끝에서부터 요소에 접근하거나 범위를 지정할 수 있음
using System;
// 먼저 사용 예제
ModernSentence s = new ModernSentence();
Console.WriteLine(s[^1]); // "fox" 출력 - 마지막 단어
string[] firstTwoWords = s[..2]; // {"The", "quick"} - 처음 두 단어
// 그 다음 클래스 정의
class ModernSentence
{
string[] words = "The quick brown fox".Split();
public string this[Index index] => words[index];
public string[] this[Range range] => words[range];
}
정적 생성자와 정적 클래스
정적 생성자(Static Constructor)
- 정적 생성자는 인스턴스별이 아닌 타입별로 한 번만 실행됨
- 타입당 하나의 정적 생성자만 정의할 수 있으며, 매개변수를 가질 수 없음
- 타입과 동일한 이름을 가져야 함
class Test
{
static Test() { Console.WriteLine("Test 클래스가 처음 사용됨"); }
}
- 정적 생성자는 다음 시점에 자동으로 실행됨:
- 타입의 인스턴스가 생성될 때
- 타입의 정적 멤버에 접근할 때
- 정적 생성자는 unsafe와 extern 수정자만 허용됨
- 처리되지 않은 예외가 발생하면 해당 타입을 더 이상 사용할 수 없게 됨
정적 필드 초기화와 실행 순서
- 정적 필드 초기화는 정적 생성자가 실행되기 직전에 수행됨
- 정적 필드는 선언된 순서대로 초기화됨
- 인스턴스 멤버에서 static 접근 = 가능
- static 멤버에서 인스턴스 접근 = 불가능
class Foo
{
public static int X = Y; // 0
public static int Y = 3; // 3
}
// 필드 초기화와 생성자 실행 순서 예제
Console.WriteLine(Foo.X); // 3
class Foo
{
public static Foo Instance = new Foo();
public static int X = 3;
Foo() { Console.WriteLine(X); } // 0 출력
}
정적 클래스(Static Class)
- static으로 표시된 클래스는 인스턴스화나 상속이 불가능함
- 정적(static) 멤버만 포함할 수 있음
- C++의 Utils를 생각하면 된다.
- 생성자도 static이어야한다.
- System.Console과 System.Math가 대표적인 예시임
static class UtilityClass
{
public static void PrintMessage()
{
Console.WriteLine("유틸리티 메서드");
}
}
소멸자(Finalizer)
- 소멸자는 클래스만 가질 수 있는 특수한 메서드임
- 가비지 컬렉터가 참조되지 않는 객체의 메모리를 회수하기 전에 실행됨
- 클래스 이름 앞에 ~를 붙여 정의함
- 가비지 컬렉터가 메모리를 삭제해주기 때문에 정의하는 일이 많이 없다.
class Class1
{
~Class1()
{
// 리소스 정리 코드
}
}
- 실제로는 Object의 Finalize 메서드를 재정의하는 것임:
protected override void Finalize()
{
try
{
// 리소스 정리 코드
}
finally
{
base.Finalize();
}
}
- 단일 문장은 식 본문으로도 작성 가능함:
class Class1
{
~Class1() => Console.WriteLine("소멸자 실행");
}
분할 타입과 메서드(Partial Types and Methods)
- partial 키워드로 타입 정의를 여러 파일로 분할할 수 있음
- 한 클래스의 코드를 여러 파일에 나누어 작성할 때 사용함
// Monster.cs
partial class Monster
{
public void Attack() { }
public void Move() { }
}
// Monster.Animation.cs
partial class Monster
{
public void PlayAttackAnimation() { }
public void PlayMoveAnimation() { }
}
- 분할 메서드는 partial 타입 내에서 메서드의 선언과 구현을 분리할 수 있게 해줌:
partial class Monster
{
// 선언
partial void OnDamaged(int damage);
}
partial class Monster
{
// 구현
partial void OnDamaged(int damage)
{
// 데미지 처리 로직
}
}
nameof 연산자
nameof 연산자의 개념
- nameof 연산자는 코드에서 사용된 식별자(identifier)의 이름을 문자열로 반환하는 연산자임
- 변수, 타입, 메서드, 프로퍼티 등 모든 프로그램 요소의 이름을 가져올 수 있음
- 정적 타입 검사를 통해 컴파일 시점에 오류를 찾을 수 있음
기본 사용법
- 가장 단순한 형태는 변수나 타입의 이름을 가져오는 것임:
int count = 123;
string name = nameof(count); // name은 "count"가 됨
Console.WriteLine(name); // 출력: count
class MyClass { }
Console.WriteLine(nameof(MyClass)); // 출력: MyClass
타입 멤버 사용
- 타입의 멤버 이름을 가져올 때는 타입을 포함하여 지정함:
// StringBuilder의 Length 프로퍼티 이름 가져오기
string name = nameof(StringBuilder.Length); // "Length"
// 전체 경로가 필요한 경우
string fullName = nameof(StringBuilder) + "." +
nameof(StringBuilder.Length); // "StringBuilder.Length"
nameof 연산자의 장점
- 문자열을 직접 입력하는 것보다 안전함
- 리팩토링 도구가 심볼 참조를 이해하고 추적할 수 있음
- 식별자 이름이 변경되면 관련된 모든 참조가 자동으로 수정됨
- 컴파일 시점에 오타나 잘못된 참조를 검출할 수 있음