C# 프로퍼티(Property) 완전 가이드: 캡슐화의 핵심 메커니즘
프로퍼티(Properties)는 클래스, 구조체, 인터페이스의 명명된 멤버입니다. 클래스나 구조체의 멤버 변수 또는 메서드를 필드(Fields)라고 합니다. 프로퍼티는 필드의 확장 개념으로, 같은 문법으로 접근하지만 접근자(Accessors)를 통해 private 필드의 값을 읽고, 쓰고, 조작합니다.
프로퍼티는 저장 위치를 직접 명명하지 않습니다. 대신 값을 읽거나(get), 쓰거나(set), 계산하는 접근자를 가집니다.
접근자 (Accessors)
프로퍼티의 접근자에는 실행문이 들어있으며, 값을 가져오기(get) 또는 설정하기(set) 위한 로직을 포함합니다. get 접근자, set 접근자, 또는 둘 다 가질 수 있습니다.
기본 프로퍼티 패턴
// Code 프로퍼티 (string)
public string Code
{
get { return code; }
set { code = value; }
}
// Name 프로퍼티 (string)
public string Name
{
get { return name; }
set { name = value; }
}
// Age 프로퍼티 (int)
public int Age
{
get { return age; }
set { age = value; }
}
get: 프로퍼티 값 반환 (읽기 전용이면 get만)
set: value 키워드로 들어오는 값 할당 (쓰기 전용이면 set만)
value: set 접근자에서 암시적 매개변수로 들어오는 값
프로퍼티 사용 예제
using System;
namespace TutorialsPoint
{
class Student
{
private string code = "N.A";
private string name = "not known";
private int age = 0;
// Code 프로퍼티
public string Code
{
get { return code; }
set { code = value; }
}
// Name 프로퍼티
public string Name
{
get { return name; }
set { name = value; }
}
// Age 프로퍼티
public int Age
{
get { return age; }
set { age = value; }
}
public override string ToString()
{
return $"Code = {Code}, Name = {Name}, Age = {Age}";
}
}
class ExampleDemo
{
public static void Main()
{
Student s = new Student();
// 프로퍼티로 값 설정 (set 접근자 호출)
s.Code = "001";
s.Name = "Zara";
s.Age = 9;
Console.WriteLine("Student Info: {0}", s); // Code = 001, Name = Zara, Age = 9
// 프로퍼티로 값 읽기 (get 접근자 호출)
s.Age += 1; // get -> 증가 -> set
Console.WriteLine("Student Info: {0}", s); // Code = 001, Name = Zara, Age = 10
Console.ReadKey();
}
}
}
자동 구현 프로퍼티 (Auto-Implemented Properties) - C# 3.0+
단순한 get/set만 필요할 때 백킹 필드(backing field)를 컴파일러가 자동 생성.
public class Student
{
// 자동 구현 프로퍼티
public string Code { get; set; }
public string Name { get; set; }
public int Age { get; set; }
// 읽기 전용 (C# 6.0+ 초기화 가능)
public string Id { get; } = Guid.NewGuid().ToString();
// 초기화 전용 (C# 9.0+ init 접근자)
public string Email { get; init; }
}
프로퍼티 고급 패턴
읽기 전용 / 쓰기 전용
// 읽기 전용 (get만)
public string FullName => $"{FirstName} {LastName}"; // 표현식 본문 (C# 6.0+)
// 쓰기 전용 (set만) - 드물게 사용
public string Password { private get; set; }
접근자 접근 수준 다르게 하기
public string Name
{
get { return name; }
protected set { name = value; } // 파생 클래스에서만 설정 가능
}
유효성 검사 로직 포함
private int age;
public int Age
{
get => age;
set
{
if (value < 0 || value > 150)
throw new ArgumentOutOfRangeException(nameof(value), "나이는 0~150 사이여야 합니다.");
age = value;
}
}
계산된 프로퍼티 (Computed Property)
public class Circle
{
public double Radius { get; set; }
// 백킹 필드 없음, 매번 계산
public double Area => Math.PI * Radius * Radius;
public double Circumference => 2 * Math.PI * Radius;
}
추상 프로퍼티 (Abstract Properties)
추상 클래스에 추상 프로퍼티를 선언하면 파생 클래스에서 반드시 구현해야 합니다.
using System;
namespace TutorialsPoint
{
public abstract class Person
{
public abstract string Name { get; set; }
public abstract int Age { get; set; }
}
class Student : Person
{
private string code = "N.A";
private string name = "N.A";
private int age = 0;
public string Code { get { return code; } set { code = value; } }
public override string Name
{
get { return name; }
set { name = value; }
}
public override int Age
{
get { return age; }
set { age = value; }
}
public override string ToString()
{
return $"Code = {Code}, Name = {Name}, Age = {Age}";
}
}
class ExampleDemo
{
public static void Main()
{
Student s = new Student();
s.Code = "001";
s.Name = "Zara";
s.Age = 9;
Console.WriteLine("Student Info: {0}", s);
s.Age += 1;
Console.WriteLine("Student Info: {0}", s);
Console.ReadKey();
}
}
}
인터페이스의 프로퍼티
public interface IPerson
{
string Name { get; set; }
int Age { get; }
// 인터페이스의 프로퍼티는 자동으로 public, 구현 없음
}
프로퍼티 vs 필드 vs 메서드
| 특성 | 필드 (Field) | 프로퍼티 (Property) | 메서드 (Method) |
| 문법 | obj.Field | obj.Property | obj.Method() |
| 로직 포함 | 불가 | 가능 (get/set) | 가능 |
| 캡슐화 | 직접 노출 | 접근 제어 가능 | 완전 은닉 |
| 인터페이스 정의 | 불가 | 가능 | 가능 |
| 리플렉션 구분 | FieldInfo | PropertyInfo | MethodInfo |
| 직렬화 기본 | 포함 안 됨 | 포함됨 | 포함 안 됨 |
모범 사례
- 공개 데이터 노출은 필드 대신 프로퍼티 사용
- 단순 래퍼면 자동 구현 프로퍼티 사용
- 유효성 검사, 지연 로드, 계산 등 로직 필요 시 전체 프로퍼티 구현
- 읽기 전용은
get만, init (C# 9.0+)으로 객체 초기화 시만 설정 가능하게
- 비싼 연산은 프로퍼티가 아닌 메서드로 (호출자 의도 명확화)
get에서 예외 던지지 않기 (디버거 평가 시 문제 발생)
핵심: 프로퍼티는 "데이터처럼 보이지만 메서드처럼 동작"하는 캡슐화의 핵심 도구입니다. 필드를 직접 공개하지 말고 프로퍼티로 감싸서 유연성과 제어력을 확보하세요.