Costruttori di struct senza parametri
Nota
Questo articolo è una specifica delle funzionalità. La specifica funge da documento di progettazione per la funzionalità. Include le modifiche specifiche proposte, insieme alle informazioni necessarie durante la progettazione e lo sviluppo della funzionalità. Questi articoli vengono pubblicati fino a quando le modifiche specifiche proposte non vengono completate e incorporate nella specifica ECMA corrente.
Potrebbero verificarsi alcune discrepanze tra la specifica di funzionalità e l'implementazione completata. Tali differenze vengono acquisite nelle note
Ulteriori informazioni sul processo di adozione degli speclet delle funzionalità nel linguaggio C# standard sono disponibili nell'articolo sulle specifiche di .
Problema del campione: https://github.com/dotnet/csharplang/issues/99
Sommario
Supportare i costruttori senza parametri e gli inizializzatori dei campi di istanza per i tipi di struct.
Motivazione
I costruttori espliciti senza parametri offrono un maggiore controllo sulle istanze costruite in modo minimo del tipo di struct.
Gli inizializzatori di campi dell'istanza consentono l'inizializzazione semplificata tra più costruttori.
Insieme, questi chiuderebbero un divario evidente tra le dichiarazioni struct
e class
.
Il supporto per gli inizializzatori di campo consente anche l'inizializzazione dei campi nelle dichiarazioni di record struct
senza implementare in modo esplicito il costruttore primario.
record struct Person(string Name)
{
public object Id { get; init; } = GetNextId();
}
Se gli inizializzatori di campo struct sono supportati per i costruttori con parametri, sembra naturale estenderlo anche ai costruttori senza parametri.
record struct Person()
{
public string Name { get; init; }
public object Id { get; init; } = GetNextId();
}
Proposta
Inizializzatori di campi dell'istanza
Le dichiarazioni di campo di istanza per uno struct possono includere inizializzatori.
Come per gli inizializzatori di campo di classe §15.5.6.3:
Un inizializzatore di variabile per un campo dell'istanza non può fare riferimento all'istanza creata.
Viene segnalato un errore se una struct ha inizializzatori di campo e non ha costruttori di istanza dichiarati, poiché non verranno eseguiti.
struct S { int F = 42; } // error: 'struct' with field initializers must include an explicitly declared constructor
Costruttori
Uno struct può dichiarare un costruttore di istanza senza parametri.
Un costruttore di istanza senza parametri è valido per tutti i tipi di struct, inclusi struct
, readonly struct
, ref struct
e record struct
.
Se non viene dichiarato alcun costruttore di istanza senza parametri, la struttura (vedere §16.4.9) ...
ha in modo implicito un costruttore di istanza senza parametri che restituisce sempre il valore risultante dall'impostazione di tutti i campi del tipo di valore al loro valore predefinito e di tutti i campi del tipo di riferimento a null.
Modificatori
Un costruttore di istanza struct senza parametri deve essere dichiarato public
.
struct S0 { } // ok
struct S1 { public S1() { } } // ok
struct S2 { internal S2() { } } // error: parameterless constructor must be 'public'
I costruttori non pubblici vengono ignorati durante l'importazione di tipi dai metadati.
I costruttori possono essere dichiarati extern
o unsafe
.
I costruttori non possono essere partial
.
Esecuzione di inizializzatori di campo
Inizializzatore di variabile di istanza (§15.11.3) viene modificato come indicato di seguito:
Quando una classe costruttore dell'istanza non dispone di un inizializzatore del costruttore oppure ha un inizializzatore del costruttore del modulo
base(...)
, tale costruttore esegue in modo implicito le inizializzazioni specificate dal variable_initializerdei campi dell'istanza dichiarati nella relativa classe. Corrisponde a una sequenza di assegnazioni eseguite immediatamente dopo l'immissione al costruttore e prima della chiamata implicita del costruttore della classe base diretta.Quando un costruttore di istanza di struct non dispone di un inizializzatore del costruttore, tale costruttore esegue in modo implicito le inizializzazioni specificate dal variable_initializerdei campi dell'istanza dichiarati nel relativo struct. Corrisponde a una sequenza di assegnazioni eseguite immediatamente all'ingresso del costruttore.
Quando un costruttore di istanza di struct dispone di un inizializzatore del costruttore
this()
che rappresenta il costruttore predefinito senza parametri , il costruttore dichiarato cancella in modo implicito tutti i campi dell'istanza ed esegue le inizializzazioni specificate dal variable_initializerdei campi dell'istanza dichiarati nel relativo struct. Immediatamente all'ingresso nel costruttore, tutti i campi di tipo valore vengono impostati sul valore predefinito e tutti i campi del tipo di riferimento vengono impostati sunull
. Subito dopo, viene eseguita una sequenza di assegnazioni corrispondenti alle variabili inizializzatrici s.
Assegnazione definita
I campi dell'istanza (diversi dai campi fixed
) devono essere assegnati in modo definitivo nei costruttori di istanza di struct che non dispongono di un inizializzatore this()
(vedere §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; }
}
Nessun inizializzatore di base()
Un inizializzatore base()
non è consentito nei costruttori di struct.
Il compilatore non genererà una chiamata al costruttore di base System.ValueType
dai costruttori dell'istanza di struct.
record struct
Viene segnalato un errore se un record struct
ha inizializzatori di campo e non contiene un costruttore primario né costruttori di istanza poiché gli inizializzatori di campo non verranno eseguiti.
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
Un record struct
con un elenco di parametri vuoto avrà un costruttore primario senza parametri.
record struct R3(); // primary .ctor: public R3() { }
record struct R4() { int F = 42; } // primary .ctor: public R4() { F = 42; }
Un costruttore esplicito senza parametri in un record struct
deve avere un inizializzatore this
che chiama il costruttore primario o un costruttore dichiarato in modo esplicito.
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;
}
Campi
Il costruttore implicito senza argomenti azzererà i campi invece di chiamare i costruttori senza argomenti dei tipi di campo. Non ci sono segnalazioni di avvisi che i costruttori di campo vengono ignorati. Nessuna modifica da 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
}
espressione default
default
ignora il costruttore senza parametri e genera un'istanza azzerata.
Nessuna modifica da C#9.
// struct S { public S() { } }
_ = default(S); // constructor ignored, no warning
new()
La creazione dell'oggetto richiama il costruttore senza parametri se è pubblico; in caso contrario, l'istanza viene azzerata. Nessuna modifica da C#9.
// public struct PublicConstructor { public PublicConstructor() { } }
// public struct PrivateConstructor { private PrivateConstructor() { } }
_ = new PublicConstructor(); // call PublicConstructor::.ctor()
_ = new PrivateConstructor(); // initobj PrivateConstructor
Un messaggio di avviso può segnalare un avviso per l'uso di new()
con un tipo di struct che ha costruttori ma nessun costruttore senza parametri.
Non verrà segnalato alcun avviso quando si usa la sostituzione di tale tipo di struct per un parametro di tipo con un vincolo new()
o struct
.
struct S { public S(int i) { } }
static T CreateNew<T>() where T : new() => new T();
_ = new S(); // no warning called
_ = CreateNew<S>(); // ok
Valori non inizializzati
Una variabile locale o un campo di un tipo struct che non è inizializzato in modo esplicito è azzerato. Il compilatore segnala un errore di assegnazione definito per uno struct non inizializzato che non è vuoto. Nessuna modifica da 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)
Allocazione di matrici
L'allocazione di matrici ignora qualsiasi costruttore senza parametri e genera elementi azzerati. Nessuna modifica da C#9.
// struct S { public S() { } }
var a = new S[1]; // constructor ignored, no warning
Valore predefinito del parametro new()
Un valore predefinito del parametro di new()
viene associato al costruttore senza parametri se public (e segnala un errore che indica che il valore non è costante); in caso contrario, l'istanza è zero.
Nessuna modifica da 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
Vincoli dei parametri di tipo: new()
e struct
I vincoli dei parametri di tipo new()
e struct
richiedono che il costruttore senza parametri sia public
se definito (vedere Vincoli soddisfacenti - §8.4.5).
Il compilatore presuppone che tutti gli struct soddisfino i vincoli new()
e struct
.
Nessuna modifica da 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()
viene emesso come chiamata a System.Activator.CreateInstance<T>()
e il compilatore presuppone che l'implementazione di CreateInstance<T>()
invochi il costruttore senza parametri di public
, se definito.
Con .NET Framework, Activator.CreateInstance<T>()
richiama il costruttore senza parametri se il vincolo è where T : new()
ma sembra ignorare il costruttore senza parametri se il vincolo è where T : struct
.
Parametri facoltativi
I costruttori con parametri facoltativi non sono considerati costruttori senza parametri. Nessuna modifica da 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
Metadati
I costruttori espliciti di istanze di struct senza parametri verranno generati nei metadati.
I costruttori di struct senza parametri pubblici verranno importati dai metadati; I costruttori di istanza di struct non pubblici verranno ignorati. Nessuna modifica da C#9.
Vedere anche
Riunioni di progettazione
- https://github.com/dotnet/csharplang/blob/main/meetings/2021/LDM-2021-04-28.md#open-questions-in-record-and-parameterless-structs
- https://github.com/dotnet/csharplang/blob/main/meetings/2021/LDM-2021-03-10.md#parameterless-struct-constructors
- https://github.com/dotnet/csharplang/blob/main/meetings/2021/LDM-2021-01-27.md#field-initializers
C# feature specifications