C#

C# 프로퍼티, 객체 초기화

윤태영(Coding) 2023. 6. 8. 12:09

< 프로퍼티(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

 

Properties - C# Programming Guide

A property in C# is a member that uses accessor methods to read, write, or compute the value of a private field as if it were a public data member.

learn.microsoft.com

Microsoft Docs - Anonymous Types (C# Programming Guide)

https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/types/anonymous-types

 

Anonymous Types

Anonymous types in C# encapsulate a set of read-only properties in an object without having to explicitly define a type. The compiler generates a name.

learn.microsoft.com

Microsoft Docs - Interfaces (C# Programming Guide)

https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/types/interfaces

 

Interfaces - define behavior for multiple types

An interface in C# contains definitions for a group of related functionalities that a non-abstract class or a struct must implement. It specifies the members and their signatures for a type that implements the interface.

learn.microsoft.com