Tipi struttura ref
(Riferimenti per C#)
È possibile usare il modificatore ref
nella dichiarazione di un tipo struttura. Le istanze di un tipo ref struct
vengono allocate nello stack e non è possibile eseguirne l'escape nell'heap gestito. Per garantire ciò, il compilatore limita l'utilizzo dei tipi ref struct
come indicato di seguito:
- Un tipo
ref struct
non può essere il tipo di elemento di una matrice. - Un tipo
ref struct
non può essere un tipo dichiarato di un campo di una classe o di un tipo nonref struct
. - Un tipo
ref struct
non può essere sottoposto a conversione boxing in System.ValueType o System.Object. - Una variabile di tipo
ref struct
non può essere acquisita in un'espressione lambda o da una funzione locale. - Prima di C# 13,
ref struct
le variabili non possono essere usate in unasync
metodo. A partire da C# 13, non è possibile usare una variabileref struct
nello stesso blocco dell'espressioneawait
in un metodoasync
. Tuttavia, è possibile usare le variabiliref struct
nei metodi sincroni, ad esempio nei metodi che restituiscono Task o Task<TResult>. - Prima di C# 13, una variabile di tipo
ref struct
non può essere usata negli iteratori. A partire da C# 13, i tipiref struct
e le variabili localiref
possono essere usati negli iteratori, purché non si tratti di segmenti di codice con l'istruzioneyield return
. - Prima di C# 13,
ref struct
non può implementare interfacce. A partire da C# 13, uno structref
può implementare le interfacce, ma deve rispettare le regole di sicurezza di riferimento. Ad esempio, un tiporef struct
non può essere convertito in un tipo di interfaccia perché richiede una conversione boxing. - Prima di C# 13, un tipo
ref struct
non può essere un argomento di tipo. A partire da C# 13, un tiporef struct
può essere l'argomento di tipo quando il parametro di tipo specificaallows ref struct
nella clausolawhere
.
In genere, un tipo ref struct
viene definito quando è necessario un tipo che includa anche membri dati di tipi ref struct
:
public ref struct CustomRef
{
public bool IsValid;
public Span<int> Inputs;
public Span<int> Outputs;
}
Per dichiarare un tipo ref struct
come readonly
, combinare i modificatori readonly
e ref
nella dichiarazione di tipo (il modificatore readonly
deve precedere il modificatore ref
):
public readonly ref struct ConversionRequest
{
public ConversionRequest(double rate, ReadOnlySpan<double> values)
{
Rate = rate;
Values = values;
}
public double Rate { get; }
public ReadOnlySpan<double> Values { get; }
}
In .NET gli esempi di un tipo ref struct
sono System.Span<T> e System.ReadOnlySpan<T>.
Campi ref
A partire da C# 11, è possibile dichiarare un campo ref
in un tipo ref struct
, come illustrato nell'esempio seguente:
public ref struct RefFieldExample
{
private ref int number;
public int GetNumber()
{
if (System.Runtime.CompilerServices.Unsafe.IsNullRef(ref number))
{
throw new InvalidOperationException("The number ref field is not initialized.");
}
return number;
}
}
Un campo ref
può avere il valore null
. Usare il metodo Unsafe.IsNullRef<T>(T) per determinare se un campo ref
è null
.
È possibile applicare il modificatore readonly
a un campo ref
nei modi seguenti:
readonly ref
: è possibile riassegnare un valore ref a un campo di questo tipo con l'operatore= ref
solo all'interno di un costruttore o di una funzione di accessoinit
. È possibile assegnare un valore con l'operatore=
in qualsiasi punto consentito dal modificatore di accesso al campo.ref readonly
: in qualsiasi momento, non è possibile assegnare un valore con l'operatore=
a tale campo. Tuttavia, è possibile riassegnare un valore ref a un campo con l'operatore= ref
.readonly ref readonly
: è possibile riassegnare un valore ref a un campo di questo tipo solo in un costruttore o in una funzione di accessoinit
. In qualsiasi momento, non è possibile assegnare un valore al campo.
Il compilatore garantisce che un riferimento archiviato in un campo ref
non sopravviva al suo referente.
La funzionalità dei campi ref
consente un'implementazione sicura dei tipi come System.Span<T>:
public readonly ref struct Span<T>
{
internal readonly ref T _reference;
private readonly int _length;
// Omitted for brevity...
}
Il tipo Span<T>
archivia un riferimento tramite il quale accede agli elementi contigui in memoria. L'uso di un riferimento consente a un'istanza Span<T>
di evitare di copiare lo spazio di archiviazione a cui fa riferimento.
Modello eliminabile
È possibile definire un tipo ref struct
eliminabile. A tale scopo, assicurarsi che un tipo ref struct
corrisponda al criterio eliminabile. Ovvero, abbia un metodo di istanza Dispose
, sia accessibile, senza parametri e abbia un tipo restituito void
. È possibile usare l'istruzione o la dichiarazione using con un'istanza di un tipo ref struct
eliminabile.
A partire da C# 13, è anche possibile implementare IDisposable nei tipi ref struct
. Tuttavia, la risoluzione dell'overload preferisce il modello eliminabile al metodo di interfaccia. Il compilatore si risolve in un metodo IDisposable.Dispose
solo quando non viene trovato un metodo Dispose
appropriato.
Restrizioni per i tipi ref struct
che implementano un'interfaccia
Queste restrizioni assicurano che un tipo ref struct
che implementa un'interfaccia rispetti le regole di sicurezza di riferimento necessarie.
ref struct
non può essere convertito in un'istanza di un'interfaccia implementata. Questa restrizione include la conversione implicita quando si usa un tiporef struct
come argomento quando il parametro è un tipo di interfaccia. La conversione comporta una conversione boxing, che viola la sicurezza di riferimento.ref struct
che implementa un'interfaccia deve implementare tutti i membri dell'interfaccia.ref struct
deve implementare i membri in cui l'interfaccia include un'implementazione predefinita.
Il compilatore applica tali restrizioni. Se si scrivono tipi ref struct
che implementano interfacce, ogni nuovo aggiornamento potrebbe includere nuovi membri dell'interfaccia predefiniti. Finché non si fornisce un'implementazione per questi nuovi metodi, l'applicazione non verrà compilata.
Importante
ref struct
che implementa un'interfaccia include la possibilità di modifiche successive che causano un'interruzione di origine e un'interruzione binaria. L'interruzione si verifica se ref struct
implementa un'interfaccia definita in un altro assembly e tale assembly fornisce un aggiornamento che aggiunge membri predefiniti a tale interfaccia.
L'interruzione di origine si verifica quando si ricompila ref struct
: deve implementare il nuovo membro, anche se è presente un'implementazione predefinita.
L'interruzione binaria si verifica se si aggiorna l'assembly esterno senza ricompilare il tipo ref struct
e il codice aggiornato chiama l'implementazione predefinita del nuovo metodo. Il runtime genera un'eccezione quando si accede al membro predefinito.
Specifiche del linguaggio C#
Per altre informazioni, vedere le sezioni seguenti delle specifiche del linguaggio C#:
Per altre informazioni sui campi ref
, vedere la nota nella proposta Miglioramenti dello struct di basso livello.