C# 프로퍼티, 객체 초기화
< 프로퍼티(Property)란? >
프로퍼티는 공개 데이터 멤버처럼 사용할 수 있지만, 실제로는 접근자(Accessor)라는 특별한 메소드들 이다.
< 프로퍼티의 구조 >
프로퍼티는 get , set Accessor로 구성된다.
get은 프로퍼티의 값을 반환하는데 사용된다. set은 새로운 값을 할당하는데 사용된다.
(C# 9 이후로는 init 액세서를 사용하여 객체 생성 시에만 값을 할당할 수 있다.)
set 또는 init에서는 value 키워드를 사용하여 할당되는 값을 정의한다.
< 백킹 필드와 프로퍼티 >
프로퍼티를 구현하는 기본 패턴 중 하나는 private 백킹 필드를 사용하여 프로퍼티 값을 설정하고 검색하는 것이다.
※백킹 필드란?
프로퍼티(Property)는 객체의 상태를 나타내는데 사용되며, 일반적으로 메서드를 통해 값을 가져오거나 설정하는 역할을 한다. 허나 백킹 필드는 이 프로퍼티의 값을 실제로 저장하는 변수라고 할 수 있다.
프로퍼티의 get 또는 set 접근자를 사용하여 값을 가져오거나 설정할 때, 이 접근자들은 내부적으로 백킹 필드를 사용하여 작업을 수행한다. 백킹 필드는 프로퍼티의 값을 저장하고, 프로퍼티의 get 접근자는 이 값을 읽어오며, set 접근자는 이 값을 변경한다.
이러한 구조는 프로퍼티가 단순히 값을 반환하거나 설정하는 것 이상의 로직을 수행할 수 있게 해준다. 예를 들어, 값을 설정하기 전에 유효성 검사를 수행하거나, 프로퍼티 값이 변경될 때 이벤트를 발생시키는 등의 작업이 가능하다.
public class Person
{
private string name; // 백킹 필드
public string Name // 프로퍼티
{
get
{
return name;
}
set
{
if (!string.IsNullOrEmpty(value))
{
name = value;
}
}
}
}
위 예제에서 name 변수는 백킹 필드이며, Name은 프로퍼티라고 할 수 있다. Name 프로퍼티를 통해 값을 설정하면, 유효성 검사를 거친 후 name 백킹 필드에 값을 저장한다. 또한, 값을 가져올 때는 name 백킹 필드에서 값을 읽어오는 것.
아래 예시에서 TimePeriod 클래스는 시간 간격을 나타낸다. 내부적으로 이 클래스는 _seconds라는 private 필드를 사용하여 시간 간격을 초 단위로 저장한다. Hours라는 read-write 프로퍼티를 통해 사용자가 시간 간격을 시간 단위로 지정할 수 있다.
public class TimePeriod
{
private double _seconds;
public double Hours
{
get { return _seconds / 3600; }
set
{
if (value < 0 || value > 24)
throw new ArgumentOutOfRangeException(nameof(value),
"The valid range is between 0 and 24.");
_seconds = value * 3600;
}
}
}
< 표현식 본문 정의 (Expression Body Definitions) >
단일 라인의 문장으로 구성된 프로퍼티 액세서는 표현식 본문을 사용하여 구현할 수 있다.
public class Person
{
private string _firstName;
private string _lastName;
public Person(string first, string last)
{
_firstName = first;
_lastName = last;
}
public string Name => $"{_firstName} {_lastName}";
}
< 자동 구현 프로퍼티 (Auto-Implemented Properties) >
get과 set 접근자가 별도의 로직 없이 값을 할당하거나 검색하는 경우, 자동 구현 프로퍼티를 사용하여 코드를 간소화할 수 있다.
public class SaleItem
{
public string Name { get; set; }
public decimal Price { get; set; }
}
< 필요한 프로퍼티 (Required Properties) (C# 11 이상) >
C# 11부터는 required 키워드를 사용하여 클라이언트 코드가 프로퍼티 또는 필드를 초기화하도록 강제할 수 있다.
public class SaleItem
{
public required string Name { get; set; }
public required decimal Price { get; set; }
}
< 메서드, 필드, 프로퍼티의 차이점 >
- 메서드(Method) : 코드 블록을 캡슐화하여 동작을 수행한다.
- 필드(Field) : 클래스의 상태를 저장하는 변수.
- 프로퍼티(Property) : 필드의 값을 조작하거나 반환하는 메서드 쌍(getter, setter)으로, 이를 통해 필드에 직접 접근하는 대신 필요한 로직을 캡슐화할 수 있다.
< 무명 형식 (Anonymous Types) >
무명 형식(Anonymous Types)은 명시적으로 타입을 정의하지 않고도 읽기 전용 프로퍼티들을 하나의 객체로 캡슐화하는 간편한 방법을 제공한다. 이 타입의 이름은 컴파일러에 의해 생성되며 소스 코드 수준에서는 사용할 수 없다. 각 프로퍼티의 타입은 컴파일러에 의해 추론된다.
무명 형식은 new 연산자와 객체 초기화자(object initializer)를 함께 사용하여 생성된다. 다음 예제는 Amount와 Message라는 두 개의 프로퍼티로 초기화된 무명 형식을 보여준다.
var v = new { Amount = 108, Message = "Hello" };
Console.WriteLine(v.Amount + v.Message);
무명 형식은 주로 쿼리 표현식의 select 절에서 사용된다. 이를 통해 소스 시퀀스의 각 객체에서 프로퍼티의 부분 집합을 반환할 수 있다. 무명 형식은 하나 이상의 public 읽기 전용 프로퍼티를 포함한다. 메소드나 이벤트와 같은 다른 종류의 클래스 멤버는 허용되지 않는다. 프로퍼티를 초기화하는 데 사용되는 표현식은 null, 익명 함수, 또는 포인터 타입이 될 수 없다.
var productQuery =
from prod in products
select new { prod.Color, prod.Price };
foreach (var v in productQuery)
{
Console.WriteLine("Color={0}, Price={1}", v.Color, v.Price);
}
무명 형식은 var를 사용하여 암시적으로 타입이 지정된 로컬 변수로 초기화하는 경우가 일반적이다.
무명 형식의 이름은 컴파일러만 알 수 있으므로 변수 선언에서 타입 이름을 지정할 수 없다.
var apple = new { Item = "apples", Price = 1.35 };
var onSale = apple with { Price = 0.79 };
무명 형식을 필드, 프로퍼티, 이벤트 또는 메서드의 반환 타입으로 선언할 수 없다. 무명 형식을 메서드에 인수로 전달하거나 저장해야 하는 경우, 일반적으로 명명된 struct 또는 클래스를 사용하는 것이 좋다.
무명 형식은 Equals 및 GetHashCode 메서드를 프로퍼티의 메서드를 기반으로 정의하기 때문에, 모든 프로퍼티가 같은 경우에만 같은 무명 형식의 두 인스턴스가 같다.
< 인터페이스와 추상 클래스에서의 프로퍼티 >
[ 인터페이스에서의 프로퍼티 ]
인터페이스에서 프로퍼티를 선언할 때는 구현을 제공하지 않고, get과 set 접근자만 지정한다.
interface IPerson {
string Name { get; set; }
int Age { get; set; }
}
[ 추상 클래스에서의 프로퍼티 ]
추상 클래스에서는 프로퍼티에 기본 구현을 제공할 수도 있고, 추상 프로퍼티로 선언하여 하위 클래스에서 구현하도록 할 수도 있다.
abstract class AbstractPerson {
public abstract string Name { get; set; }
public int Age { get; set; }
}
Reference:
Microsoft Docs - Properties (C# Programming Guide)
https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/properties
Microsoft Docs - Anonymous Types (C# Programming Guide)
https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/types/anonymous-types
Microsoft Docs - Interfaces (C# Programming Guide)
https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/types/interfaces