Microsoft MVP성태의 닷넷 이야기
닷넷: 2295. C# 12 - 기본 생성자(Primary constructors) (책 오타 수정) [링크 복사], [링크+제목 복사],
조회: 6798
글쓴 사람
정성태 (seongtaejeong at gmail.com)
홈페이지
첨부 파일
 
(연관된 글이 1개 있습니다.)

C# 12 - 기본 생성자(Primary constructors) (책 오타 수정)

"영귤" 님의 피드백으로 알게 되었습니다. 문제는, 내용을 단순히 몇 글자 수정하는 것으로 끝내려니 문맥이 좀 이상해져서 조금 많이 수정한 다음 이렇게 공개 글로 남깁니다. (이 내용은 향후 개정판이 나온다면 반영될 것입니다.)

참고로, 몇년 전부터는 글로 옮기기 전에 먼저 이렇게 정리해 두었는데,

C# 12 - 기본 생성자(Primary Constructors)
; https://www.sysnet.pe.kr/2/0/13339

오히려 저 글이 더 preview 내용을 바탕으로 쓴 것임에도 불구하고 정정할 내용이 없군요. ^^;




생성자 메서드는 인스턴스의 초기화 외에도 기타 관련 코드를 포함할 수도 있겠지만 경우에 따라서는 아래의 코드처럼 단순히 내부 상태를 설정하는 용도로만 정의할 때도 많다.

PersonDTO p = new PersonDTO("John", 42);

public class PersonDTO
{
    // 내부 멤버를 초기화하는 코드만 포함하는 생성자 메서드
    public PersonDTO(string name, int age)
    {
        Name = name;
        Age = age;
    }
    public string Name { get; set; }
    public int Age { get; set; }
}

자칫 반복적일 수 있는 이런 식의 코드는 C# 9부터 나온 record 문법을 활용하면 단 1줄의 코드로 줄이는 것이 가능하다.

public record class PersonDTO(string Name, int Age);
// 또는 public record struct PersonDTO(string Name, int Age);

이처럼 편리한 record의 생성자 메서드 정의 방식을 C# 12부터 일반 타입에 도입한 것이 바로 기본 생성자 (Primary constructor)다.

public class PersonDTO(string name, int age); // 기본 생성자를 정의하는 신규 문법

// 또는 public struct PersonDTO(string name, int age);

단지 record와는 다르게 기본 생성자의 매개변수가 항상 내부 필드로 구현되지는 않는다. 예를 들어 아래와 같이 기본 생성자를 자동 구현 속성과 연동하면,

// 기본 생성자의 매개변수를 자동 구현 속성에서 사용하는 예제
public class PersonDTO(string name, int age)
{
    public string Name { get; private set; } = name;
    public int Age { get; private set; } = age;
}

내부적으로 이렇게 바뀌어서 컴파일된다.

// C# 컴파일러에 의해 자동 작성된 기본 생성자
public class PersonDTO
{
    public PersonDTO(string name, int age) // 확장된 기본 생성자
    {
        this.Name = name;
        this.Age = age;
    }
    public string Name { get; private set; }
    public int Age { get; private set; }
}

반면 대응하는 속성이나 필드 없이 직접 멤버 메서드에서 사용하는 경우에는,

// 기본 생성자의 매개변수를 멤버 메서드에서 직접 사용하는 예제
public class PersonDTO(string name, int age)
{
    public override string ToString() => $"{name}: {age}";
}

내부적으로 필드를 자동으로 생성한 다음 생성자를 통해 초기화 코드를 연결하고 ToString 메서드에서 사용하도록 확장한다.

// C# 컴파일러에 의해 자동 작성된 기본 생성자
public class PersonDTO
{
    string _name; // 필드로 확장된 매개변수 (실제 필드 이름은 이와 다름)
    int _age;

    public PersonDTO(string name, int age) // 확장된 기본 생성자
    {
        _name = name;
        _age = age;
    }

    public override string ToString() => $"{_name}: {_age}";
}

따라서, 매개변수와는 별도로 내부에서 어떻게 연동했느냐에 따라 클래스의 크기가 달라진다.

마지막으로 기본 생성자를 사용할 때 유의할 점이 있다면 다른 생성자를 함께 정의하는 경우 반드시 기본 생성자를 경유하는 this 호출 코드를 넣어야 한다는 점이다. 다음 예제는 name만을 받는 생성자를 별도로 정의하면서 기본 생성자 측으로 this 호출을 넘기고 있다.

PersonDTO p = new PersonDTO("John"); // 기본 생성자가 아닌 사용자가 만든 생성자 호출

public struct PersonDTO(string name, int age)
{
    public PersonDTO(string name) : this(name, 0) { }

    public string Name = name;
    public int Age { get; init; } = age;
}

정리하자면, 개념상 기본 생성자에 지정한 매개변수들을 class/struct 정의 문법 내에서 유효하게 사용할 수 있는 변수라고 볼 수 있다.




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 8/29/2024]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2024-08-29 11시42분
"내부에서 어떻게 연동했느냐에 따라 클래스의 크기가 달라진다."는 말씀이
public class PersonDTO(string name, int age)
{
    public string Name { get; private set; } = name;
    public int Age { get; private set; } = age;
}

public class PersonDTO(string name, int age)
{
    public override string ToString() => $"{name}: {age}";
}
가 크기가 달라진다는 말씀이 아니라

public class PersonDTO(string name, int age)
{
    public override string ToString() => $"{name}: {age}";
}
를 만들면서 필드든 프로퍼티든 생성되지 않을 것처럼 오해하지 말라는 말씀이지요?
그사람
2024-08-29 12시07분
넵, 같은 이야기일 듯합니다. 생성자에서 연동하는 방식이 아니면 숨겨진 필드를 이용해 연동하기 때문에 마찬가지로 클래스의 크기가 달라지는 것입니다.
정성태
2024-08-29 06시07분
퇴근해서 다시 보니, 말씀하신 의도를 이제야 이해했습니다. ^^; 결국 그걸 굳이 언급할 필요가 없었던 것 같습니다. 어떤 식으로든 사용하면 내부 필드로 매칭될 수밖에 없고, 만약 그 2가지 방법 중 어느 하나라도 쓰지 않으면 C# 컴파일러가 친절하게 경고까지 내주니 현실적으로 클래스의 크기가 달라질 일은 없을 듯합니다. ^^ (본문에서, 그 부분을 취소 처리했습니다.)
정성태

... 76  77  78  79  80  81  82  83  84  [85]  86  87  88  89  90  ...
NoWriterDateCnt.TitleFile(s)
11808정성태2/8/201921891디버깅 기술: 123. windbg - 닷넷 응용 프로그램의 메모리 누수 분석
11807정성태1/29/201919797Windows: 156. 가상 디스크의 용량을 복구 파티션으로 인해 늘리지 못하는 경우 [4]
11806정성태1/29/201919486디버깅 기술: 122. windbg - 덤프 파일로부터 PID와 환경 변수 등의 정보를 구하는 방법
11805정성태1/28/201921566.NET Framework: 806. C# - int []와 object []의 차이로 이해하는 제네릭의 필요성 [4]파일 다운로드1
11804정성태1/24/201919440Windows: 155. diskpart - remove letter 이후 재부팅 시 다시 드라이브 문자가 할당되는 경우
11803정성태1/10/201918327디버깅 기술: 121. windbg - 닷넷 Finalizer 스레드가 멈춰있는 현상
11802정성태1/7/201920064.NET Framework: 805. 두 개의 윈도우를 각각 실행하는 방법(Windows Forms, WPF)파일 다운로드1
11801정성태1/1/201921424개발 환경 구성: 427. Netsh의 네트워크 모니터링 기능 [3]
11800정성태12/28/201820459오류 유형: 509. WCF 호출 오류 메시지 - System.ServiceModel.CommunicationException: Internal Server Error
11799정성태12/19/201822186.NET Framework: 804. WPF(또는 WinForm)에서 UWP UI 구성 요소 사용하는 방법 [3]파일 다운로드1
11798정성태12/19/201821034개발 환경 구성: 426. vcpkg - "Building vcpkg.exe failed. Please ensure you have installed Visual Studio with the Desktop C++ workload and the Windows SDK for Desktop C++"
11797정성태12/19/201817045개발 환경 구성: 425. vcpkg - CMake Error: Problem with archive_write_header(): Can't create '' 빌드 오류
11796정성태12/19/201817246개발 환경 구성: 424. vcpkg - "File does not have expected hash" 오류를 무시하는 방법
11795정성태12/19/201820603Windows: 154. PowerShell - Zone 별로 DNS 레코드 유형 정보 조회 [1]
11794정성태12/16/201816666오류 유형: 508. Get-AzureWebsite : Request to a downlevel service failed.
11793정성태12/16/201819245개발 환경 구성: 423. NuGet 패키지 제작 - Native와 Managed DLL을 분리하는 방법 [1]
11792정성태12/11/201819021Graphics: 34. .NET으로 구현하는 OpenGL (11) - Per-Pixel Lighting파일 다운로드1
11791정성태12/11/201819047VS.NET IDE: 130. C/C++ 프로젝트의 시작 프로그램으로 .NET Core EXE를 지정하는 경우 닷넷 디버깅이 안 되는 문제 [1]
11790정성태12/11/201817552오류 유형: 507. Could not save daemon configuration to C:\ProgramData\Docker\config\daemon.json: Access to the path 'C:\ProgramData\Docker\config' is denied.
11789정성태12/10/201831161Windows: 153. C# - USB 장치의 연결 및 해제 알림을 위한 WM_DEVICECHANGE 메시지 처리 [2]파일 다운로드2
11788정성태12/4/201817407오류 유형: 506. SqlClient - Value was either too large or too small for an Int32.Couldn't store <2151292191> in ... Column
11787정성태11/29/201821560Graphics: 33. .NET으로 구현하는 OpenGL (9), (10) - OBJ File Format, Loading 3D Models파일 다운로드1
11786정성태11/29/201818498오류 유형: 505. OpenGL.NET 예제 실행 시 "Managed Debugging Assistant 'CallbackOnCollectedDelegate'" 예외 발생
11785정성태11/21/201820881디버깅 기술: 120. windbg 분석 사례 - ODP.NET 사용 시 Finalizer에서 System.AccessViolationException 예외 발생으로 인한 비정상 종료
11784정성태11/18/201820094Graphics: 32. .NET으로 구현하는 OpenGL (7), (8) - Matrices and Uniform Variables, Model, View & Projection Matrices파일 다운로드1
11783정성태11/18/201818241오류 유형: 504. 윈도우 환경에서 docker가 설치된 컴퓨터 간의 ping IP 주소 풀이 오류
... 76  77  78  79  80  81  82  83  84  [85]  86  87  88  89  90  ...