Quick Reference
class는 변수 대입 시 객체가 아니라 참조를 복사하므로 두 변수가 같은 인스턴스의 변경을 공유할 수 있습니다. identity가 중요한 수명 있는 객체에는 class를, 같은 데이터면 같다고 비교해야 하는 전달 값에는 record를, 작고 값 복사가 자연스러운 데이터에는 struct를 검토합니다.
public class Player
{
public string Name { get; set; }
public int Score { get; private set; }
public Player(string name) => Name = name;
public void AddScore(int points) => Score += points;
}
// 생성자로 인스턴스 생성
var player = new Player("Mina");
player.AddScore(10);
Console.WriteLine(player.Score); // 10
// object initializer (public setter가 있는 프로퍼티만 가능)
var p2 = new Player("Jin") { Name = "Jin Park" };문법
어떤 형태가 있나
클래스를 사용할 때는 다음 네 가지를 함께 봅니다.
public class Player
{
public string Name { get; set; } = "";
public int Score { get; private set; }
public Player(string name) => Name = name;
public void AddScore(int points) => Score += points;
}
var player = new Player("Mina");
player.AddScore(10);class선언new로 인스턴스 생성- 프로퍼티와 메서드 호출
static멤버는 클래스 이름으로 접근
클래스는 참조 타입
class는 참조 타입입니다. 변수 대입은 객체 전체 복사가 아니라 같은 객체를 가리키는 참조를 복사합니다. 따라서 한 참조로 상태를 바꾸면 다른 참조에서도 보입니다. 객체가 항상 "힙에 있다"는 식의 저장 위치는 언어 계약이 아니라 런타임 구현 세부이므로, 참조 복사와 identity를 기준으로 이해합니다.
var a = new Player("Mina");
var b = a; // 같은 객체를 참조
b.AddScore(50);
Console.WriteLine(a.Score); // 50 — a도 바뀜!
// 동일 여부 확인
bool sameRef = object.ReferenceEquals(a, b); // true
bool sameIdentity = object.ReferenceEquals(a, b); // 항상 identity 비교
bool sameByOperator = a == b; // 기본 class는 identity, type이 ==를 overload하면 달라질 수 있음필드, 프로퍼티, 메서드의 역할
필드는 객체의 원시 데이터를 저장하는 변수입니다. 프로퍼티는 필드에 대한 접근을 제어하는 게이트입니다. 메서드는 객체의 동작을 정의합니다.
public class BankAccount
{
private decimal _balance; // 필드 — 직접 노출하지 않음
// 프로퍼티 — 접근 제어 + 유효성 검사 가능
public decimal Balance
{
get => _balance;
private set
{
if (value < 0) throw new InvalidOperationException("잔액 부족");
_balance = value;
}
}
// 메서드 — 입력과 상태를 함께 검증
public void Deposit(decimal amount)
{
if (amount <= 0) throw new ArgumentOutOfRangeException(nameof(amount));
Balance = checked(Balance + amount);
}
public void Withdraw(decimal amount)
{
if (amount <= 0) throw new ArgumentOutOfRangeException(nameof(amount));
if (amount > Balance) throw new InvalidOperationException("잔액 부족");
Balance = checked(Balance - amount);
}
}필드를 직접 public으로 열어두면 외부 코드가 언제든 값을 바꿀 수 있어 불변식(invariant) 이 깨지기 쉽습니다.
정적 멤버 — 인스턴스 없이 쓰는 멤버
static 멤버는 클래스 자체에 속합니다. 인스턴스를 만들지 않아도 사용할 수 있고, 모든 인스턴스가 같은 값을 공유합니다.
public class Counter
{
public static int Total { get; private set; } = 0; // 정적 프로퍼티
public Counter() => Total++; // 생성될 때마다 증가
public static void Reset() => Total = 0; // 정적 메서드
}
var c1 = new Counter();
var c2 = new Counter();
Console.WriteLine(Counter.Total); // 2
Counter.Reset();선택 기준
| 상황 | 더 적합한 선택 |
|---|---|
| 고유한 상태와 수명이 있는 엔티티 | class |
| 값 자체의 내용 비교가 중요한 데이터 | record 또는 명시적 equality를 가진 타입 |
| 작고 복사 비용이 낮고 value semantics가 자연스러운 데이터 | struct |
var a = new Player("Mina");
var b = a;
// ❌ 새 객체가 생긴다고 착각하기 쉽다
b.AddScore(10);
Console.WriteLine(a.Score); // 10클래스 구성과 비교
| 개념 | 의미 |
|---|---|
| 클래스 | 객체의 구조(필드, 프로퍼티)와 동작(메서드) 정의 |
| 인스턴스 | new로 생성된 실제 객체 |
| 참조 타입 | 변수 대입 시 같은 객체를 공유 |
static | 인스턴스 없이 클래스 자체에 속한 멤버 |
| 캡슐화 | private 필드 + public 프로퍼티/메서드로 접근 제어 |
주의할 점
var a = b;처럼 클래스 변수를 복사하면 새 객체가 아니라 같은 인스턴스를 가리킵니다. 독립적인 복사본이 필요하면 복사 생성자나 명시적 복제 메서드를 구현합니다. record의 with도 얕은 복사이므로, 내부 컬렉션·참조 멤버까지 독립 복사되는 것은 아닙니다.
필드를 모두 public으로 열어두는 것은 캡슐화를 포기하는 것입니다. 외부에서 객체의 상태를 직접 수정할 수 있게 되면 유효성 검사, 이벤트 발행, 캐시 무효화 같은 부가 로직을 끼워 넣기 어려워집니다.
참고 링크
3 sources