Udostępnij za pośrednictwem


Konstruktory struktury bez parametrów

Notatka

Ten artykuł jest specyfikacją funkcji. Specyfikacja służy jako dokument projektowy dla funkcji. Zawiera proponowane zmiany specyfikacji wraz z informacjami wymaganymi podczas projektowania i opracowywania funkcji. Te artykuły są publikowane do momentu sfinalizowania proponowanych zmian specyfikacji i włączenia ich do obecnej specyfikacji ECMA.

Mogą wystąpić pewne rozbieżności między specyfikacją funkcji a ukończoną implementacją. Te różnice są przechwytywane w odpowiednich spotkania projektowego języka (LDM).

Więcej informacji na temat procesu wdrażania specyfikacji funkcji można znaleźć w standardzie języka C# w artykule dotyczącym specyfikacji .

Streszczenie

Obsługa konstruktorów bez parametrów i inicjatorów pól wystąpień dla typów struktur.

Motywacja

Jawne konstruktory bez parametrów zapewniają większą kontrolę nad minimalnie skonstruowanymi wystąpieniami typu struktury. Inicjalizatory pól instancji umożliwiłyby uproszczone inicjowanie w wielu konstruktorach. Razem zamkną one oczywistą różnicę między deklaracjami struct i class.

Obsługa inicjatorów pól umożliwia również inicjowanie pól w deklaracjach record struct bez jawnego implementowania konstruktora podstawowego.

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

Jeśli inicjatory pól struktury są obsługiwane dla konstruktorów z parametrami, wydaje się naturalne, że można je rozszerzyć również na konstruktory bez parametrów.

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

Propozycja

Inicjatory pól wystąpienia

Deklaracje pól wystąpienia dla struktury mogą zawierać inicjalizatory.

Podobnie jak w przypadku inicjatorów pól klasy §15.5.6.3:

Inicjator zmiennej dla pola wystąpienia nie może odwoływać się do tworzonego wystąpienia.

Zgłaszany jest błąd, jeśli struktura ma inicjatory pól, a nie ma zadeklarowanych konstruktorów wystąpień, ponieważ wtedy inicjatory pól nie zostaną uruchomione.

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

Konstruktorzy

Struktura może zadeklarować konstruktor instancji bez parametrów.

Konstruktor wystąpienia bez parametrów jest prawidłowy dla wszystkich rodzajów struktur, w tym struct, readonly struct, ref structi record struct.

Jeżeli nie zadeklarowano konstruktora instancji bez parametrów, struktura (zobacz §16.4.9) ...

Niejawnie ma konstruktor instancji bez parametrów, który zawsze zwraca wartość powstałą przez ustawienie wszystkich pól typu wartości na ich domyślną wartość. Wszystkie pola typu odwołania są ustawione na null.

Modyfikatory

Konstruktor struktury wystąpienia bez parametrów musi być zadeklarowany public.

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

Konstruktory inne niż publiczne są ignorowane podczas importowania typów z metadanych.

Konstruktory można zadeklarować extern lub unsafe. Konstruktorów nie można używać jako partial.

Wykonywanie inicjatorów pól

inicjalizatory zmiennych instancji (§15.11.3) są zmodyfikowane w następujący sposób:

Gdy konstruktora klasy wystąpienia nie ma inicjatora konstruktora lub ma inicjator konstruktora formularza base(...), ten konstruktor niejawnie wykonuje inicjalizacji określone przez variable_initializers pól wystąpienia zadeklarowanych w swojej klasie. Odpowiada to sekwencji przypisań, które są wykonywane natychmiast po wejściu do konstruktora i przed niejawnym wywołaniem bezpośredniego konstruktora klasy bazowej.

Gdy konstruktor instancji struktury nie ma inicjatora konstruktora, ten konstruktor niejawnie wykonuje inicjalizacje określone przez variable_initializerpól instancji zadeklarowanych w jego strukturze. Koreluje to z sekwencją przypisań, które są wykonywane natychmiast po wejściu do konstruktora.

Gdy konstruktor wystąpienia struktury ma inicjator konstruktora this(), który reprezentuje domyślny konstruktor bez parametrów, zadeklarowany konstruktor niejawnie czyści wszystkie pola wystąpienia i wykonuje inicjalizacje określone przez variable_initializerpól wystąpienia zadeklarowanych w jego strukturze. Natychmiast po wpisie do konstruktora wszystkie pola typu wartości są ustawione na ich wartość domyślną, a wszystkie pola typu odwołania są ustawione na null. Natychmiast po tym jest wykonywana sekwencja przypisań odpowiadających variable_initializers.

Konkretne przypisanie

Pola wystąpienia (inne niż pola fixed) muszą być zdecydowanie przypisane w konstruktorach wystąpienia struktury, które nie mają inicjatora this() (zobacz §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; }
}

Brak inicjatora base()

Inicjalizator base() jest niedozwolony w konstruktorach struktury.

Kompilator nie generuje wywołania konstruktora bazowego System.ValueType z konstruktorów instancji struktury.

record struct

Zgłaszany jest błąd, jeśli record struct zawiera inicjatory pól i nie zawiera konstruktora podstawowego ani żadnych konstruktorów wystąpień, ponieważ inicjatory pól nie zostaną uruchomione.

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 z pustą listą parametrów będzie miał konstruktor główny bez parametrów.

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

Konstruktor jawny bez parametrów w record struct musi mieć inicjator this, który wywołuje konstruktor główny lub konstruktor jawnie zadeklarowany.

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;
}

Pola

Niejawnie zdefiniowany konstruktor bez parametrów wyzeruje pola zamiast wywoływać jakiekolwiek konstruktory bez parametrów dla typów pól. Nie zgłoszono żadnych ostrzeżeń, że konstruktory pól są ignorowane. Brak zmian od 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
}

wyrażenie default

default ignoruje konstruktor bez parametrów i generuje zerowane wystąpienie. Brak zmian od C#9.

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

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

new()

Tworzenie obiektu wywołuje konstruktor bez parametrów, jeśli jest publiczny; w przeciwnym razie wystąpienie ma wartość zero. Brak zmian w stosunku do C#9.

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

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

Fala ostrzegawcza może zgłosić ostrzeżenie dotyczące użycia new() z typem struktury, który ma konstruktory, ale nie konstruktor bez parametrów. Podczas używania zastępowania takiego typu struktury dla parametru typu z ograniczeniem new() lub struct nie zostanie zgłoszone żadne ostrzeżenie.

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

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

Niezainicjowane wartości

Lokalna zmienna lub pole typu struktury, które nie jest jawnie zainicjowane, jest wyzerowane. Kompilator zgłasza określony błąd przypisania dla niezainicjowanej struktury, która nie jest pusta. Bez zmian od 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)

Alokacja tablicy

Alokacja tablicy ignoruje dowolny konstruktor bez parametrów i generuje zero elementów. Brak zmian względem C#9.

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

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

Wartość domyślna parametru new()

Wartość domyślna parametru new() wiąże się z konstruktorem bez parametrów, jeśli jest publiczny (i zgłasza błąd, że wartość nie jest stała); w przeciwnym razie wystąpienie ma wartość zero. Bez zmian od 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

Ograniczenia parametrów typu: new() i struct

Ograniczenia parametrów typu new() i struct wymagają, aby konstruktor bez parametrów, jeśli jest zdefiniowany, był public (zobacz Satysfakcjonujące ograniczenia — §8.4.5).

Kompilator zakłada, że wszystkie struktury spełniają new() i struct ograniczenia. Bez zmian od 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() jest emitowany jako wywołanie System.Activator.CreateInstance<T>(), a kompilator zakłada, że implementacja CreateInstance<T>() wywołuje konstruktor bez parametrów public, jeśli jest zdefiniowany.

za pomocą programu .NET Framework Activator.CreateInstance<T>() wywołuje konstruktor bez parametrów, jeśli ograniczenie jest where T : new(), ale wydaje się ignorować konstruktor bez parametrów, jeśli ograniczenie jest where T : struct.

Parametry opcjonalne

Konstruktory z parametrami opcjonalnymi nie są uważane za konstruktory bez parametrów. Brak zmian względem 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

Metadane

Jawne konstruktory wystąpień bez parametrów będą emitowane do metadanych.

Konstruktory wystąpienia struktury bez parametrów publicznych zostaną zaimportowane z metadanych; Konstruktory wystąpień innych niż publiczne zostaną zignorowane. Bez zmian względem C#9.

Zobacz też

Spotkania projektowe