다음을 통해 공유


매개 변수가 없는 구조체 생성자

메모

이 문서는 기능 사양입니다. 사양은 기능의 디자인 문서 역할을 합니다. 여기에는 기능 디자인 및 개발 중에 필요한 정보와 함께 제안된 사양 변경 내용이 포함됩니다. 이러한 문서는 제안된 사양 변경이 완료되고 현재 ECMA 사양에 통합될 때까지 게시됩니다.

기능 사양과 완료된 구현 간에 약간의 불일치가 있을 수 있습니다. 이러한 차이는 관련LDM(언어 디자인 모임) 노트에서 캡처됩니다.

사양문서에서 기능 speclet을 C# 언어 표준에 채택하는 과정에 대해 자세히 알아볼 수 있습니다.

챔피언 이슈: https://github.com/dotnet/csharplang/issues/99

요약

구조체 형식에 대한 매개 변수가 없는 생성자 및 인스턴스 필드 이니셜라이저를 지원합니다.

동기

명시적 매개 변수가 없는 생성자는 구조체 형식의 최소 생성 인스턴스를 보다 효율적으로 제어할 수 있습니다. 인스턴스 필드 이니셜라이저는 여러 생성자에서 간소화된 초기화를 허용합니다. 이렇게 하면 struct 선언과 class 선언 간의 명백한 차이가 닫힙니다.

또한 필드 이니셜라이저를 지원하면 기본 생성자를 명시적으로 구현하지 않고도 record struct 선언에서 필드를 초기화할 수 있습니다.

record struct Person(string Name)
{
    public object Id { get; init; } = GetNextId();
}

매개 변수가 있는 생성자에 구조체 필드 이니셜라이저가 지원되는 경우 매개 변수가 없는 생성자로도 확장하는 것이 당연해 보입니다.

record struct Person()
{
    public string Name { get; init; }
    public object Id { get; init; } = GetNextId();
}

제안

인스턴스 필드 이니셜라이저

구조체에 대한 인스턴스 필드 선언에는 이니셜라이저가 포함될 수 있습니다.

클래스 필드 이니셜라이저와 마찬가지로 §15.5.6.3 .

인스턴스 필드의 변수 이니셜라이저는 생성되는 인스턴스를 참조할 수 없습니다.

필드 이니셜라이저가 실행되지 않으므로 구조체에 필드 이니셜라이저가 있고 선언된 인스턴스 생성자가 없는 경우 오류가 보고됩니다.

struct S { int F = 42; } // error: 'struct' with field initializers must include an explicitly declared constructor

생성자

구조체는 매개 변수가 없는 인스턴스 생성자를 선언할 수 있습니다.

매개 변수가 없는 인스턴스 생성자는 struct, readonly struct, ref structrecord struct포함한 모든 구조체 종류에 유효합니다.

매개 변수가 없는 인스턴스 생성자가 선언되지 않은 경우 구조체(§16.4.9참조) ...

에는 항상 모든 값 형식 필드를 기본값으로 설정하고 모든 참조 형식 필드를 null로 설정하여 발생하는 값을 반환하는 매개 변수가 없는 인스턴스 생성자가 암시적으로 있습니다.

한정자

매개 변수가 없는 인스턴스 구조체 생성자는 public선언해야 합니다.

struct S0 { }                   // ok
struct S1 { public S1() { } }   // ok
struct S2 { internal S2() { } } // error: parameterless constructor must be 'public'

메타데이터에서 형식을 가져올 때는 공용이 아닌 생성자가 무시됩니다.

생성자는 extern 또는 unsafe선언할 수 있습니다. 생성자는 partial이 될 수 없습니다.

필드 이니셜라이저 실행

인스턴스 변수 이니셜라이저(§15.11.3)는 다음과 같이 수정됩니다.

클래스 인스턴스 생성자에 생성자 이니셜라이저가 없거나 base(...)양식의 생성자 이니셜라이저가 있는 경우 해당 생성자는 클래스에 선언된 인스턴스 필드의 variable_initializer지정한 초기화를 암시적으로 수행합니다. 이는 생성자에 진입할 때와 직접 기본 클래스 생성자의 암시적 호출 전에 즉시 실행되는 할당 시퀀스에 해당합니다.

구조체 인스턴스 생성자에 생성자 이니셜라이저가 없는 경우, 해당 생성자는 구조체에 선언된 인스턴스 필드의 variable_initializer의 지정된 초기화를 암시적으로 수행합니다. 이는 생성자에 진입할 때 즉시 실행되는 일련의 할당에 해당합니다.

구조체 인스턴스 생성자에 기본 매개 변수 없는 생성자나타내는 this() 생성자 이니셜라이저가 있는 경우 선언된 생성자는 모든 인스턴스 필드를 암시적으로 지우고 구조체에 선언된 인스턴스 필드의 variable_initializer지정한 초기화를 수행합니다. 생성자에 진입하자마자, 모든 값 형식 필드는 기본값으로 설정되고 모든 참조 형식 필드는 null으로 설정됩니다. 그 직후 variable_initializer에 대응하는 일련의 할당이 수행됩니다.

명확한 과제

fixed 필드 이외의 인스턴스 필드는 this() 이니셜라이저가 없는 구조체 인스턴스 생성자에 확실히 할당되어야 합니다(§16.4.9참조).

struct S0 // ok
{
    int x;
    object y;
}

struct S1 // error: 'struct' with field initializers must include an explicitly declared constructor
{
    int x = 1;
    object y;
}

struct S2
{
    int x = 1;
    object y;
    public S2() { } // error in C# 10 (valid starting in C# 11): field 'y' must be assigned
}

struct S3 // ok
{
    int x = 1;
    object y;
    public S3() { y = 2; }
}

base() 이니셜라이저 없음

구조체 생성자에서는 base() 이니셜라이저가 허용되지 않습니다.

컴파일러는 구조체 인스턴스 생성자에서 기본 System.ValueType 생성자에 대한 호출을 내보내지 않습니다.

record struct

record struct에 필드 이니셜라이저가 포함되어 있고 기본 생성자나 인스턴스 생성자가 없을 경우, 필드 이니셜라이저가 실행되지 않으므로 오류가 보고됩니다.

record struct R0;                  // ok
record struct R1 { int F = 42; }   // error: 'struct' with field initializers must include an explicitly declared constructor
record struct R2() { int F = 42; } // ok
record struct R3(int F);           // ok

매개 변수 목록이 비어 있는 record struct 매개 변수가 없는 기본 생성자가 있습니다.

record struct R3();                // primary .ctor: public R3() { }
record struct R4() { int F = 42; } // primary .ctor: public R4() { F = 42; }

record struct 명시적 매개 변수 없는 생성자에는 주 생성자 또는 명시적으로 선언된 생성자를 호출하는 this 이니셜라이저가 있어야 합니다.

record struct R5(int F)
{
    public R5() { }                  // error: must have 'this' initializer that calls explicit .ctor
    public R5(object o) : this() { } // ok
    public int F =  F;
}

필드

암시적으로 정의된 매개 변수 없는 생성자는 필드 형식에 대해 매개 변수가 없는 생성자를 호출하지 않고 필드가 0이 됩니다. 필드 생성자가 무시된다는 경고는 보고되지 않습니다. C#9에서 변경되지 않습니다.

struct S0
{
    public S0() { }
}

struct S1
{
    S0 F; // S0 constructor ignored
}

struct S<T> where T : struct
{
    T F; // constructor (if any) ignored
}

default

default 매개 변수가 없는 생성자를 무시하고 0으로 된 인스턴스를 생성합니다. C#9에서 변경되지 않습니다.

// struct S { public S() { } }

_ = default(S); // constructor ignored, no warning

new()

개체 만들기는 매개 변수가 없는 생성자(public인 경우)를 호출합니다. 그렇지 않으면 인스턴스가 0이 됩니다. C#9에서 변경되지 않습니다.

// public struct PublicConstructor { public PublicConstructor() { } }
// public struct PrivateConstructor { private PrivateConstructor() { } }

_ = new PublicConstructor();  // call PublicConstructor::.ctor()
_ = new PrivateConstructor(); // initobj PrivateConstructor

경고 파형은 생성자는 있지만 매개 변수가 없는 생성자가 없는 구조체 타입에서 new() 사용에 대해 경고를 보고할 수 있습니다. 형식 매개 변수에 이러한 구조체 형식을 new() 또는 struct 제약 조건으로 대체하는 경우 경고가 보고되지 않습니다.

struct S { public S(int i) { } }
static T CreateNew<T>() where T : new() => new T();

_ = new S();        // no warning called
_ = CreateNew<S>(); // ok

초기화되지 않은 값

명시적으로 초기화되지 않은 구조체 형식의 로컬 또는 필드는 0입니다. 컴파일러는 비어 있지 않은 초기화되지 않은 구조체에 대한 명확한 할당 오류를 보고합니다. C#9에서 변경되지 않습니다.

NoConstructor s1;
PublicConstructor s2;
s1.ToString(); // error: use of unassigned local (unless type is empty)
s2.ToString(); // error: use of unassigned local (unless type is empty)

배열 할당

배열 할당은 매개 변수가 없는 생성자를 무시하고 0개 요소를 생성합니다. C#9에서 변경되지 않습니다.

// struct S { public S() { } }

var a = new S[1]; // constructor ignored, no warning

매개 변수 기본값 new()

new() 매개 변수 기본값은 public인 경우 매개 변수가 없는 생성자에 바인딩되며 값이 상수가 아니라는 오류를 보고합니다. 그렇지 않으면 인스턴스가 0이 됩니다. C#9에서 변경되지 않습니다.

// public struct PublicConstructor { public PublicConstructor() { } }
// public struct PrivateConstructor { private PrivateConstructor() { } }

static void F1(PublicConstructor s1 = new()) { }  // error: default value must be constant
static void F2(PrivateConstructor s2 = new()) { } // ok: initobj

형식 매개 변수 제약 조건: new()struct

new()struct 타입 매개 변수 제약 조건은 정의된 경우, 매개변수가 없는 생성자가 public이어야 합니다(제약 조건 만족 부분 - §8.4.5 참조).

컴파일러는 모든 구조체가 new()struct 제약 조건을 충족하는 것으로 가정합니다. C#9에서 변경되지 않습니다.

// public struct PublicConstructor { public PublicConstructor() { } }
// public struct InternalConstructor { internal InternalConstructor() { } }

static T CreateNew<T>() where T : new() => new T();
static T CreateStruct<T>() where T : struct => new T();

_ = CreateNew<PublicConstructor>();      // ok
_ = CreateStruct<PublicConstructor>();   // ok

_ = CreateNew<InternalConstructor>();    // compiles; may fail at runtime
_ = CreateStruct<InternalConstructor>(); // compiles; may fail at runtime

new T()System.Activator.CreateInstance<T>()호출로 내보내지며, 컴파일러는 CreateInstance<T>()의 구현이 정의되어 있는 경우 public의 매개변수가 없는 생성자를 호출한다고 가정합니다.

.NET Framework에서는 제약 조건이 where T : new()인 경우 Activator.CreateInstance<T>() 매개 변수 없는 생성자를 호출하며, 제약 조건이 where T : struct인 경우에는 매개 변수가 없는 생성자를 무시하는 것처럼 보입니다.

선택적 매개 변수

선택적 매개 변수가 있는 생성자는 매개 변수가 없는 생성자로 간주되지 않습니다. C#9에서 변경되지 않습니다.

struct S1 { public S1(string s = "") { } }
struct S2 { public S2(params object[] args) { } }

_ = new S1(); // ok: ignores constructor
_ = new S2(); // ok: ignores constructor

메타데이터

명시적 매개 변수가 없는 구조체 인스턴스 생성자는 메타데이터로 내보내됩니다.

공용 매개 변수가 없는 구조체 인스턴스 생성자는 메타데이터에서 가져옵니다. public이 아닌 구조체 인스턴스 생성자는 무시됩니다. C#9에서 변경되지 않습니다.

또한 참조

디자인 회의