Imposizione del tempo di compilazione per la sicurezza per i tipi simili a RefCompile time enforcement of safety for ref-like types

IntroduzioneIntroduction

Il motivo principale per le regole di sicurezza aggiuntive quando si gestiscono tipi come Span<T> e ReadOnlySpan<T> è che tali tipi devono essere limitati allo stack di esecuzione.The main reason for the additional safety rules when dealing with types like Span<T> and ReadOnlySpan<T> is that such types must be confined to the execution stack.

Esistono due motivi per cui Span<T> e i tipi simili devono essere tipi di solo stack.There are two reasons why Span<T> and similar types must be a stack-only types.

  1. Span<T> è semanticamente uno struct che contiene un riferimento e un intervallo (ref T data, int length) .Span<T> is semantically a struct containing a reference and a range - (ref T data, int length). Indipendentemente dall'implementazione effettiva, le Scritture in tale struct non sarebbero atomiche.Regardless of actual implementation, writes to such struct would not be atomic. Il "strappo" simultaneo di tale struct potrebbe causare la length mancata corrispondenza di data , causando accessi fuori intervallo e violazioni di indipendenza dai tipi, che in definitiva potrebbero causare il danneggiamento dell'heap GC nel codice apparentemente "sicuro".Concurrent "tearing" of such struct would lead to the possibility of length not matching the data, causing out-of-range accesses and type-safety violations, which ultimately could result in GC heap corruption in seemingly "safe" code.
  2. Alcune implementazioni di Span<T> contengono letteralmente un puntatore gestito in uno dei relativi campi.Some implementations of Span<T> literally contain a managed pointer in one of its fields. I puntatori gestiti non sono supportati come campi di oggetti heap e il codice che gestisce per inserire un puntatore gestito nell'heap GC in genere si arresta in modo anomalo in fase di JIT.Managed pointers are not supported as fields of heap objects and code that manages to put a managed pointer on the GC heap typically crashes at JIT time.

Se le istanze di Span<T> sono vincolate a esistere solo nello stack di esecuzione, vengono risolti tutti i problemi precedenti.All the above problems would be alleviated if instances of Span<T> are constrained to exist only on the execution stack.

Un ulteriore problema si verifica a causa della composizione.An additional problem arises due to composition. È in genere consigliabile compilare tipi di dati più complessi che incorporano Span<T> istanze di e ReadOnlySpan<T> .It would be generally desirable to build more complex data types that would embed Span<T> and ReadOnlySpan<T> instances. Tali tipi compositi dovrebbero essere struct e condividere tutti i rischi e i requisiti di Span<T> .Such composite types would have to be structs and would share all the hazards and requirements of Span<T>. Di conseguenza, le regole di sicurezza descritte di seguito dovrebbero essere visualizzate come applicabile all'intera gamma di tipi simili a Ref.As a result the safety rules described here should be viewed as applicable to the whole range of ref-like types.

La specifica del linguaggio bozza ha lo scopo di garantire che i valori di un tipo simile a ref vengano eseguiti solo nello stack.The draft language specification is intended to ensure that values of a ref-like type occur only on the stack.

Tipi generalizzati ref-like nel codice sorgenteGeneralized ref-like types in source code

ref-like gli struct sono contrassegnati in modo esplicito nel codice sorgente usando il ref modificatore:ref-like structs are explicitly marked in the source code using ref modifier:

ref struct TwoSpans<T>
{
    // can have ref-like instance fields
    public Span<T> first;
    public Span<T> second;
} 

// error: arrays of ref-like types are not allowed. 
TwoSpans<T>[] arr = null;

La designazione di uno struct come ref-like consentirà allo struct di avere campi di istanza di tipo ref e renderà disponibili anche tutti i requisiti di tipi simili a Ref applicabili allo struct.Designating a struct as ref-like will allow the struct to have ref-like instance fields and will also make all the requirements of ref-like types applicable to the struct.

Rappresentazione dei metadati o struct di tipo RefMetadata representation or ref-like structs

Gli struct di tipo ref verranno contrassegnati con l'attributo System. Runtime. CompilerServices. IsRefLikeAttribute .Ref-like structs will be marked with System.Runtime.CompilerServices.IsRefLikeAttribute attribute.

L'attributo verrà aggiunto alle librerie di base comuni, ad esempio mscorlib .The attribute will be added to common base libraries such as mscorlib. Se l'attributo non è disponibile, il compilatore genererà uno interno in modo analogo ad altri attributi incorporati su richiesta, ad esempio IsReadOnlyAttribute .In a case if the attribute is not available, compiler will generate an internal one similarly to other embedded-on-demand attributes such as IsReadOnlyAttribute.

Viene eseguita una misura aggiuntiva per impedire l'uso di struct di tipo Ref nei compilatori che non conoscono le regole di sicurezza (sono inclusi i compilatori C# precedenti a quello in cui è implementata questa funzionalità).An additional measure will be taken to prevent the use of ref-like structs in compilers not familiar with the safety rules (this includes C# compilers prior to the one in which this feature is implemented).

Non avendo altre alternative valide che funzionano nei compilatori obsoleti senza manutenzione, Obsolete viene aggiunto un attributo con una stringa nota a tutti gli struct simili a Ref.Having no other good alternatives that work in old compilers without servicing, an Obsolete attribute with a known string will be added to all ref-like structs. I compilatori che sanno come usare i tipi di tipo ref ignoreranno questo particolare formato di Obsolete .Compilers that know how to use ref-like types will ignore this particular form of Obsolete.

Rappresentazione tipica dei metadati:A typical metadata representation:

    [IsRefLike]
    [Obsolete("Types with embedded references are not supported in this version of your compiler.")]
    public struct TwoSpans<T>
    {
       // . . . .
    }

Nota: non è l'obiettivo di fare in modo che qualsiasi uso di tipi simili a Ref nei compilatori precedenti non riesca 100%.NOTE: it is not the goal to make it so that any use of ref-like types on old compilers fails 100%. Questo è difficile da raggiungere e non è strettamente necessario.That is hard to achieve and is not strictly necessary. Ad esempio, esiste sempre un modo per aggirare l' Obsolete uso del codice dinamico o, ad esempio, la creazione di una matrice di tipi simili a Ref tramite reflection.For example there would always be a way to get around the Obsolete using dynamic code or, for example, creating an array of ref-like types through reflection.

In particolare, se l'utente vuole effettivamente inserire un Obsolete Deprecated attributo o su un tipo simile a ref, non sarà possibile scegliere un valore diverso da quello predefinito, perché non Obsolete è possibile applicare l'attributo più di una volta.In particular, if user wants to actually put an Obsolete or Deprecated attribute on a ref-like type, we will have no choice other than not emitting the predefined one since Obsolete attribute cannot be applied more than once..

Esempi:Examples:

SpanLikeType M1(ref SpanLikeType x, Span<byte> y)
{
    // this is all valid, unconcerned with stack-referring stuff
    var local = new SpanLikeType(y);
    x = local;
    return x;
}

void Test1(ref SpanLikeType param1, Span<byte> param2)
{
    Span<byte> stackReferring1 = stackalloc byte[10];
    var stackReferring2 = new SpanLikeType(stackReferring1);

    // this is allowed
    stackReferring2 = M1(ref stackReferring2, stackReferring1);

    // this is NOT allowed
    stackReferring2 = M1(ref param1, stackReferring1);

    // this is NOT allowed
    param1 = M1(ref stackReferring2, stackReferring1);

    // this is NOT allowed
    param2 = stackReferring1.Slice(10);

    // this is allowed
    param1 = new SpanLikeType(param2);

    // this is allowed
    stackReferring2 = param1;
}

ref SpanLikeType M2(ref SpanLikeType x)
{
    return ref x;
}

ref SpanLikeType Test2(ref SpanLikeType param1, Span<byte> param2)
{
    Span<byte> stackReferring1 = stackalloc byte[10];
    var stackReferring2 = new SpanLikeType(stackReferring1);

    ref var stackReferring3 = M2(ref stackReferring2);

    // this is allowed
    stackReferring3 = M1(ref stackReferring2, stackReferring1);

    // this is allowed
    M2(ref stackReferring3) = stackReferring2;

    // this is NOT allowed
    M1(ref param1) = stackReferring2;

    // this is NOT allowed
    param1 = stackReferring3;

    // this is NOT allowed
    return ref stackReferring3;

    // this is allowed
    return ref param1;
}


Specifica del linguaggio bozzaDraft language specification

Di seguito viene descritto un set di regole di sicurezza per i tipi simili ref struct a ref per garantire che i valori di questi tipi vengano eseguiti solo nello stack.Below we describe a set of safety rules for ref-like types (ref structs) to ensure that values of these types occur only on the stack. Un set di regole di sicurezza diverso e più semplice è possibile se le variabili locali non possono essere passate per riferimento.A different, simpler set of safety rules would be possible if locals cannot be passed by reference. Questa specifica consente inoltre la riassegnazione sicura delle variabili locali Ref.This specification would also permit the safe reassignment of ref locals.

PanoramicaOverview

Si associa a ogni espressione in fase di compilazione il concetto di ambito a cui l'espressione può eseguire l'escape, ovvero "safe-to-escape".We associate with each expression at compile-time the concept of what scope that expression is permitted to escape to, "safe-to-escape". Analogamente, per ogni lvalue si mantiene un concetto dell'ambito a cui è consentito l'escape di un riferimento a esso, ovvero "Ref-safe-to-escape".Similarly, for each lvalue we maintain a concept of what scope a reference to it is permitted to escape to, "ref-safe-to-escape". Per un'espressione lvalue specificata, potrebbero essere diversi.For a given lvalue expression, these may be different.

Si tratta di una procedura analoga alla "restituzione sicura" della funzionalità variabili locali Ref, ma con granularità più fine.These are analogous to the "safe to return" of the ref locals feature, but it is more fine-grained. Dove il "safe-to-return" di un'espressione registra solo se (o meno) può sfuggire al metodo di inclusione nel suo complesso, i record di tipo safe-to-escape per cui è possibile eseguire l'escape, ovvero l'ambito di cui non è possibile evitare l'escape.Where the "safe-to-return" of an expression records only whether (or not) it may escape the enclosing method as a whole, the safe-to-escape records which scope it may escape to (which scope it may not escape beyond). Il meccanismo di sicurezza di base viene applicato come indicato di seguito.The basic safety mechanism is enforced as follows. Data un'assegnazione da un'espressione E1 con un ambito sicuro a escape S1, a un'espressione (lvalue) E2 con ambito safe-to-escape S2, si tratta di un errore se S2 è un ambito più ampio rispetto a S1.Given an assignment from an expression E1 with a safe-to-escape scope S1, to an (lvalue) expression E2 with safe-to-escape scope S2, it is an error if S2 is a wider scope than S1. In base alla costruzione, i due ambiti S1 e S2 si trovano in una relazione di annidamento, perché un'espressione valida è sempre sicura per essere restituita da un ambito che include l'espressione.By construction, the two scopes S1 and S2 are in a nesting relationship, because a legal expression is always safe-to-return from some scope enclosing the expression.

Per il momento in cui è sufficiente, ai fini dell'analisi, per supportare solo due ambiti esterni al metodo e l'ambito di primo livello del metodo.For the time being it is sufficient, for the purpose of the analysis, to support just two scopes - external to the method, and top-level scope of the method. Ciò è dovuto al fatto che non è possibile creare valori di tipo Ref con ambiti interni e le variabili locali Ref non supportano la riassegnazione.That is because ref-like values with inner scopes cannot be created and ref locals do not support re-assignment. Le regole, tuttavia, possono supportare più di due livelli di ambito.The rules, however, can support more than two scope levels.

Di seguito sono riportate le regole precise per il calcolo dello stato di sicurezza per la restituzione di un'espressione e le regole relative alla legalità delle espressioni.The precise rules for computing the safe-to-return status of an expression, and the rules governing the legality of expressions, follow.

Ref-safe-to-escaperef-safe-to-escape

Ref-safe-to-escape è un ambito, che include un'espressione lvalue, a cui è possibile eseguire l'escape di un riferimento a lvalue in modo sicuro.The ref-safe-to-escape is a scope, enclosing an lvalue expression, to which it is safe for a ref to the lvalue to escape to. Se tale ambito è l'intero metodo, si afferma che un riferimento a lvalue è sicuro per restituire dal metodo.If that scope is the entire method, we say that a ref to the lvalue is safe to return from the method.

da Safe a escapesafe-to-escape

Il tipo safe-to-escape è un ambito, che racchiude un'espressione a cui è sicuro che il valore venga sottoposto a escape.The safe-to-escape is a scope, enclosing an expression, to which it is safe for the value to escape to. Se tale ambito è l'intero metodo, si afferma che il valore è Safe per restituire dal metodo.If that scope is the entire method, we say that the value is safe to return from the method.

Un'espressione il cui tipo non è un ref struct tipo è safe-to-Return dall'intero metodo contenitore.An expression whose type is not a ref struct type is safe-to-return from the entire enclosing method. In caso contrario, si fa riferimento alle regole seguenti.Otherwise we refer to the rules below.

ParametriParameters

Un lvalue che designa un parametro formale è ref-safe-to-escape (per riferimento) come indicato di seguito:An lvalue designating a formal parameter is ref-safe-to-escape (by reference) as follows:

  • Se il parametro è un ref out parametro, o in , è ref-safe-to-escape dall'intero metodo (ad esempio tramite un' return ref istruzione); in caso contrario,.If the parameter is a ref, out, or in parameter, it is ref-safe-to-escape from the entire method (e.g. by a return ref statement); otherwise
  • Se il parametro è il this parametro di un tipo struct, è di tipo ref-safe-to-escape nell'ambito di primo livello del metodo (ma non dall'intero metodo); Esempio diIf the parameter is the this parameter of a struct type, it is ref-safe-to-escape to the top-level scope of the method (but not from the entire method itself); Sample
  • In caso contrario, il parametro è un parametro di valore ed è ref-safe-to-escape per l'ambito di primo livello del metodo, ma non dal metodo stesso.Otherwise the parameter is a value parameter, and it is ref-safe-to-escape to the top-level scope of the method (but not from the method itself).

Un'espressione che è un rvalue che designa l'uso di un parametro formale è safe-to-escape (per valore) dall'intero metodo (ad esempio tramite un' return istruzione).An expression that is an rvalue designating the use of a formal parameter is safe-to-escape (by value) from the entire method (e.g. by a return statement). Questo vale anche per il this parametro.This applies to the this parameter as well.

Variabili localiLocals

Un lvalue che designa una variabile locale è ref-safe-to-escape (per riferimento) come indicato di seguito:An lvalue designating a local variable is ref-safe-to-escape (by reference) as follows:

  • Se la variabile è una ref variabile, il relativo ref-safe-to-escape viene tratto da ref-safe-to-escape della relativa espressione di inizializzazione; in caso contrario,.If the variable is a ref variable, then its ref-safe-to-escape is taken from the ref-safe-to-escape of its initializing expression; otherwise
  • La variabile è ref-safe-to-escape nell'ambito in cui è stata dichiarata.The variable is ref-safe-to-escape the scope in which it was declared.

Un'espressione che è un rvalue che designa l'uso di una variabile locale è safe-to-escape (per valore) come indicato di seguito:An expression that is an rvalue designating the use of a local variable is safe-to-escape (by value) as follows:

  • Tuttavia, la regola generale precedente, un oggetto locale il cui tipo non è un ref struct tipo è safe-to-Return dall'intero metodo contenitore.But the general rule above, a local whose type is not a ref struct type is safe-to-return from the entire enclosing method.
  • Se la variabile è una variabile di iterazione di un foreach ciclo, l'ambito safe-to-escape della variabile è uguale a quello di safe-to-escape dell' foreach espressione del ciclo.If the variable is an iteration variable of a foreach loop, then the variable's safe-to-escape scope is the same as the safe-to-escape of the foreach loop's expression.
  • Un oggetto locale di ref struct tipo e non inizializzato al momento della dichiarazione è sicuro per essere restituito dall'intero metodo contenitore.A local of ref struct type and uninitialized at the point of declaration is safe-to-return from the entire enclosing method.
  • In caso contrario, il tipo della variabile è un ref struct tipo e la dichiarazione della variabile richiede un inizializzatore.Otherwise the variable's type is a ref struct type, and the variable's declaration requires an initializer. L'ambito di sicurezza della variabile è identico a quello del relativo inizializzatore.The variable's safe-to-escape scope is the same as the safe-to-escape of its initializer.

Riferimento ai campiField reference

Un lvalue che designa un riferimento a un campo, e.F , è ref-safe-to-escape (per riferimento) come indicato di seguito:An lvalue designating a reference to a field, e.F, is ref-safe-to-escape (by reference) as follows:

  • Se e è di un tipo riferimento, è ref-safe-to-escape dall'intero metodo; in caso contrario,If e is of a reference type, it is ref-safe-to-escape from the entire method; otherwise
  • Se e è di un tipo di valore, il relativo ref-safe-to-escape viene tratto da ref-safe-to-escape di e .If e is of a value type, its ref-safe-to-escape is taken from the ref-safe-to-escape of e.

Un rvalue che designa un riferimento a un campo, e.F , dispone di un ambito di tipo safe-to-escape che corrisponde alla sicurezza di escape di e .An rvalue designating a reference to a field, e.F, has a safe-to-escape scope that is the same as the safe-to-escape of e.

Operatori che includono ?:Operators including ?:

L'applicazione di un operatore definito dall'utente viene considerata come una chiamata al metodo.The application of a user-defined operator is treated as a method invocation.

Per un operatore che restituisce un rvalue, ad esempio e1 + e2 o c ? e1 : e2 , il valore safe-to-escape del risultato è l'ambito più stretto tra i tipi di sicurezza di escape degli operandi dell'operatore.For an operator that yields an rvalue, such as e1 + e2 or c ? e1 : e2, the safe-to-escape of the result is the narrowest scope among the safe-to-escape of the operands of the operator. Di conseguenza, per un operatore unario che restituisce un rvalue, ad esempio +e , il safe-to-escape del risultato è la sicurezza di escape dell'operando.As a consequence, for a unary operator that yields an rvalue, such as +e, the safe-to-escape of the result is the safe-to-escape of the operand.

Per un operatore che restituisce un lvalue, ad esempio c ? ref e1 : ref e2For an operator that yields an lvalue, such as c ? ref e1 : ref e2

  • ref-safe-to-escape del risultato è l'ambito più restrittivo tra ref-safe-to-escape degli operandi dell'operatore.the ref-safe-to-escape of the result is the narrowest scope among the ref-safe-to-escape of the operands of the operator.
  • il safe-to-escape degli operandi deve essere d'accordo e questo è il safe-to-escape del lvalue risultante.the safe-to-escape of the operands must agree, and that is the safe-to-escape of the resulting lvalue.

Chiamata al metodoMethod invocation

Un lvalue risultante da una chiamata al metodo che restituisce un riferimento e1.M(e2, ...) è ref-safe-to-escape il più piccolo degli ambiti seguenti:An lvalue resulting from a ref-returning method invocation e1.M(e2, ...) is ref-safe-to-escape the smallest of the following scopes:

  • Intero metodo contenitoreThe entire enclosing method
  • ref-safe-to-escape di tutte le ref out espressioni di argomento e (escluso il ricevitore)the ref-safe-to-escape of all ref and out argument expressions (excluding the receiver)
  • Per ogni in parametro del metodo, se esiste un'espressione corrispondente che è un lvalue, il relativo ref-safe-to-escape, in caso contrario l'ambito di inclusione più vicinoFor each in parameter of the method, if there is a corresponding expression that is an lvalue, its ref-safe-to-escape, otherwise the nearest enclosing scope
  • il carattere di escape sicuro per tutte le espressioni di argomento (incluso il ricevitore)the safe-to-escape of all argument expressions (including the receiver)

Nota: l'ultimo punto elenco è necessario per gestire il codice, ad esempioNote: the last bullet is necessary to handle code such as

var sp = new Span(...)
return ref sp[0];

oppureor

return ref M(sp, 0);

Un rvalue risultante da una chiamata al metodo e1.M(e2, ...) è di tipo safe-to-escape dal più piccolo degli ambiti seguenti:An rvalue resulting from a method invocation e1.M(e2, ...) is safe-to-escape from the smallest of the following scopes:

  • Intero metodo contenitoreThe entire enclosing method
  • il carattere di escape sicuro per tutte le espressioni di argomento (incluso il ricevitore)the safe-to-escape of all argument expressions (including the receiver)

RvalueAn Rvalue

Un rvalue è ref-safe-to-escape dall'ambito di inclusione più vicino.An rvalue is ref-safe-to-escape from the nearest enclosing scope. Questo problema si verifica ad esempio in una chiamata, ad esempio M(ref d.Length) dove d è di tipo dynamic .This occurs for example in an invocation such as M(ref d.Length) where d is of type dynamic. È inoltre coerente con (e probabilmente sussume) la gestione degli argomenti corrispondenti ai in parametri.It is also consistent with (and perhaps subsumes) our handling of arguments corresponding to in parameters.

Chiamate di proprietàProperty invocations

Chiamata di una proprietà ( get o set ) considerata come chiamata di metodo del metodo sottostante dalle regole precedenti.A property invocation (either get or set) it treated as a method invocation of the underlying method by the above rules.

stackalloc

Un'espressione stackalloc è un rvalue che è sicuro da escludere dall'ambito di primo livello del metodo (ma non dall'intero metodo).A stackalloc expression is an rvalue that is safe-to-escape to the top-level scope of the method (but not from the entire method itself).

Chiamate al costruttoreConstructor invocations

Un' new espressione che richiama un costruttore rispetta le stesse regole di una chiamata al metodo considerata per restituire il tipo costruito.A new expression that invokes a constructor obeys the same rules as a method invocation that is considered to return the type being constructed.

Inoltre, safe-to-escape non è più ampio del più piccolo tra quelli di tipo safe-to-escape di tutti gli argomenti o operandi delle espressioni dell'inizializzatore di oggetto, in modo ricorsivo se è presente l'inizializzatore.In addition safe-to-escape is no wider than the smallest of the safe-to-escape of all arguments/operands of the object initializer expressions, recursively, if initializer is present.

Costruttore SpanSpan constructor

Il linguaggio si basa sul fatto Span<T> che non è presente un costruttore nel formato seguente:The language relies on Span<T> not having a constructor of the following form:

void Example(ref int x)
{
    // Create a span of length one
    var span = new Span<int>(ref x); 
}

Un costruttore di questo tipo consente Span<T> di usare come campi non distinguibili da un ref campo.Such a constructor makes Span<T> which are used as fields indistinguishable from a ref field. Le regole di sicurezza descritte in questo documento dipendono dai ref campi che non sono un costrutto valido in C# o .NET.The safety rules described in this document depend on ref fields not being a valid construct in C# or .NET.

Espressioni defaultdefault expressions

Un' default espressione è sicura per l'escape dall'intero metodo contenitore.A default expression is safe-to-escape from the entire enclosing method.

Vincoli della linguaLanguage Constraints

Si vuole garantire che nessuna ref variabile locale e nessuna variabile di tipo faccia ref struct riferimento alla memoria dello stack o a variabili che non sono più attive.We wish to ensure that no ref local variable, and no variable of ref struct type, refers to stack memory or variables that are no longer alive. Sono pertanto presenti i vincoli di linguaggio seguenti:We therefore have the following language constraints:

  • Non è possibile rimuovere un parametro ref, né un oggetto locale Ref né un parametro o una variabile locale di un ref struct tipo in un'espressione lambda o in una funzione locale.Neither a ref parameter, nor a ref local, nor a parameter or local of a ref struct type can be lifted into a lambda or local function.

  • Né un parametro ref né un parametro di un ref struct tipo possono essere un argomento in un metodo iteratore o un async metodo.Neither a ref parameter nor a parameter of a ref struct type may be an argument on an iterator method or an async method.

  • Né un oggetto locale Ref né un oggetto locale di un ref struct tipo possono essere nell'ambito nel punto di un' yield return istruzione o di un' await espressione.Neither a ref local, nor a local of a ref struct type may be in scope at the point of a yield return statement or an await expression.

  • Un ref struct tipo non può essere utilizzato come argomento di tipo o come tipo di elemento in un tipo di tupla.A ref struct type may not be used as a type argument, or as an element type in a tuple type.

  • Un ref struct tipo non può essere il tipo dichiarato di un campo, ad eccezione del fatto che può essere il tipo dichiarato di un campo di istanza di un altro ref struct .A ref struct type may not be the declared type of a field, except that it may be the declared type of an instance field of another ref struct.

  • Un ref struct tipo non può essere il tipo di elemento di una matrice.A ref struct type may not be the element type of an array.

  • Un valore di un ref struct tipo non può essere boxed:A value of a ref struct type may not be boxed:

    • Non esiste alcuna conversione da un ref struct tipo al tipo object o al tipo System.ValueType .There is no conversion from a ref struct type to the type object or the type System.ValueType.
    • ref structNon è possibile dichiarare un tipo per implementare alcuna interfacciaA ref struct type may not be declared to implement any interface
    • Nessun metodo di istanza dichiarato in object o in System.ValueType , ma non sottoposto a override in un ref struct tipo, può essere chiamato con un destinatario di quel ref struct tipo.No instance method declared in object or in System.ValueType but not overridden in a ref struct type may be called with a receiver of that ref struct type.
    • Nessun metodo di istanza di un ref struct tipo può essere acquisito dalla conversione del metodo in un tipo delegato.No instance method of a ref struct type may be captured by method conversion to a delegate type.
  • Per una riassegnazione Ref ref e1 = ref e2 , il ref-safe-to-escape di e2 deve avere un ambito almeno uguale a quello di ref-safe-to-escape di e1 .For a ref reassignment ref e1 = ref e2, the ref-safe-to-escape of e2 must be at least as wide a scope as the ref-safe-to-escape of e1.

  • Per un'istruzione ref return return ref e1 , ref-safe-to-escape di e1 deve essere ref-safe-to-escape dall'intero metodo.For a ref return statement return ref e1, the ref-safe-to-escape of e1 must be ref-safe-to-escape from the entire method. (TODO: è necessaria anche una regola che e1 deve essere di tipo safe-to-escape dall'intero metodo o è ridondante?)(TODO: Do we also need a rule that e1 must be safe-to-escape from the entire method, or is that redundant?)

  • Per un'istruzione return return e1 , l'oggetto safe-to-escape di e1 deve essere safe-to-escape dall'intero metodo.For a return statement return e1, the safe-to-escape of e1 must be safe-to-escape from the entire method.

  • Per un'assegnazione e1 = e2 , se il tipo di e1 è un ref struct tipo, il livello di sicurezza di escape di deve essere almeno uguale a quello e2 di un ambito come safe-to-escape di e1 .For an assignment e1 = e2, if the type of e1 is a ref struct type, then the safe-to-escape of e2 must be at least as wide a scope as the safe-to-escape of e1.

  • Per una chiamata a un metodo se è presente ref un out argomento o di un ref struct tipo (incluso il ricevitore), con la sicurezza di escape E1, nessun argomento (incluso il ricevitore) può avere una sicurezza di escape più stretta di E1.For a method invocation if there is a ref or out argument of a ref struct type (including the receiver), with safe-to-escape E1, then no argument (including the receiver) may have a narrower safe-to-escape than E1. EsempioSample

  • Una funzione locale o una funzione anonima non può fare riferimento a un parametro o locale di ref struct tipo dichiarato in un ambito di inclusione.A local function or anonymous function may not refer to a local or parameter of ref struct type declared in an enclosing scope.

Problema aperto: È necessaria una regola che consenta di generare un errore quando è necessario eseguire il versamento di un valore dello stack di un ref struct tipo in un'espressione await, ad esempio nel codiceOpen Issue: We need some rule that permits us to produce an error when needing to spill a stack value of a ref struct type at an await expression, for example in the code

Foo(new Span<int>(...), await e2);

SpiegazioniExplanations

Queste spiegazioni ed esempi illustrano il motivo per cui sono presenti molte delle regole di sicurezza precedentiThese explanations and samples help explain why many of the safety rules above exist

Gli argomenti del metodo devono corrispondereMethod Arguments Must Match

Quando si richiama un metodo in cui è presente out un ref parametro, che ref struct include il ricevitore, è necessario che tutte ref struct le richieste abbiano la stessa durata.When invoking a method where there is an out, ref parameter that is a ref struct including the receiver then all of the ref struct need to have the same lifetime. Questa operazione è necessaria perché C# deve prendere tutte le decisioni relative alla sicurezza della durata in base alle informazioni disponibili nella firma del metodo e la durata dei valori nel sito di chiamata.This is necessary because C# must make all of its decisions around lifetime safety based on the information available in the signature of the method and the lifetime of the values at the call site.

Quando sono presenti ref parametri che possono essere ref struct sostituiti con il contenuto.When there are ref parameters that are ref struct then there is the possiblity they could swap around their contents. Quindi, nel sito di chiamata è necessario assicurarsi che tutti questi potenziali swap siano compatibili.Hence at the call site we must ensure all of these potential swaps are compatible. Se la lingua non è stata applicata, sarà possibile scrivere codice errato come quello riportato di seguito.If the language didn't enforce that then it will allow for bad code like the following.

void M1(ref Span<int> s1)
{
    Span<int> s2 = stackalloc int[1];
    Swap(ref s1, ref s2);
}

void Swap(ref Span<int> x, ref Span<int> y)
{
    // This will effectively assign the stackalloc to the s1 parameter and allow it
    // to escape to the caller of M1
    ref x = ref y; 
}

La restrizione sul ricevitore è necessaria perché mentre nessun contenuto è Ref-safe-to-escape, può archiviare i valori specificati.The restriction on the receiver is necessary because while none of its contents are ref-safe-to-escape it can store the provided values. Ciò significa che con durate non corrispondenti è possibile creare un foro di sicurezza dei tipi nel modo seguente:This means with mismatched lifetimes you could create a type safety hole in the following way:

ref struct S
{
    public Span<int> Span;

    public void Set(Span<int> span)
    {
        Span = span;
    }
}

void Broken(ref S s)
{
    Span<int> span = stackalloc int[1];

    // The result of a stackalloc is now stored in s.Span and escaped to the caller
    // of Broken
    s.Set(span); 
}

Struttura EscapeStruct This Escape

Per quanto riguarda le regole di sicurezza, il this valore in un membro di istanza viene modellato come parametro per il membro.When it comes to span safety rules, the this value in an instance member is modeled as a parameter to the member. A questo punto struct , il tipo di this è in realtà ref S Where in a è class semplicemente S (per i membri di un class / struct denominato s).Now for a struct the type of this is actually ref S where in a class it's simply S (for members of a class / struct named S).

thisDispone tuttavia di regole di escape diverse da quelle degli altri ref parametri.Yet this has different escaping rules than other ref parameters. In particolare, non è Ref-safe-to-escape mentre altri parametri sono:Specifically it is not ref-safe-to-escape while other parameters are:

ref struct S
{ 
    int Field;

    // Illegal because `this` isn't safe to escape as ref
    ref int Get() => ref Field;

    // Legal
    ref int GetParam(ref int p) => ref p;
}

Il motivo di questa restrizione è in realtà sufficiente per la struct chiamata del membro.The reason for this restriction actually has little to do with struct member invocation. Esistono alcune regole che devono essere elaborate in relazione alla chiamata del membro sui membri in struct cui il ricevitore è un rvalue.There are some rules that need to be worked out with respect to member invocation on struct members where the receiver is an rvalue. Ma questo approccio è molto intuitivo.But that is very approachable.

Il motivo di questa restrizione riguarda effettivamente la chiamata all'interfaccia.The reason for this restriction is actually about interface invocation. In particolare, si tratta di un valore che indica se l'esempio seguente non deve o non deve essere compilato;Specifically it comes down to whether or not the following sample should or should not compile;

interface I1
{
    ref int Get();
}

ref int Use<T>(T p)
    where T : I1
{
    return ref p.Get();
}

Si consideri il caso T in cui viene creata un'istanza di come struct .Consider the case where T is instantiated as a struct. Se il this parametro è Ref-safe-to-escape, il valore restituito di p.Get può puntare allo stack (in particolare può essere un campo all'interno del tipo di cui è stata creata un'istanza di T ).If the this parameter is ref-safe-to-escape then the return of p.Get could point to the stack (specifically it could be a field inside of the instantiated type of T). Ciò significa che il linguaggio non può consentire la compilazione di questo esempio perché potrebbe restituire un oggetto ref a una posizione dello stack.That means the language could not allow this sample to compile as it could be returning a ref to a stack location. D'altra parte, se this non è Ref-safe-to-escape, p.Get non può fare riferimento allo stack e pertanto è sicuro restituire.On the other hand if this is not ref-safe-to-escape then p.Get cannot refer to the stack and hence it's safe to return.

Questo è il motivo per cui l'evasione di this in un struct è molto importante per le interfacce.This is why the escapability of this in a struct is really all about interfaces. Questa operazione può essere eseguita, ma presenta un compromesso.It can absolutely be made to work but it has a trade off. Alla fine, la progettazione si è rivelata a favore di rendere più flessibili le interfacce.The design eventually came down in favor of making interfaces more flexible.

In futuro, tuttavia, è possibile che ci si rilassi.There is potential for us to relax this in the future though.

ConsiderazioniFuture Considerations

Lunghezza di un intervallo <T> rispetto ai valori RefLength one Span<T> over ref values

Sebbene non sia attualmente valido, esistono casi in cui la creazione di una lunghezza di un' Span<T> istanza su un valore può risultare utile:Though not legal today there are cases where creating a length one Span<T> instance over a value would be beneficial:

void RefExample()
{
    int x = ...;

    // Today creating a length one Span<int> requires a stackalloc and a new 
    // local
    Span<int> span1 = stackalloc [] { x };
    Use(span1);
    x = span1[0]; 

    // Simpler to just allow length one span
    var span2 = new Span<int>(ref x);
    Use(span2);
}

Questa funzionalità risulta più interessante se si rilevano le restrizioni sui buffer a dimensione fissa in quanto consentono di ottenere Span<T> una lunghezza ancora maggiore.This feature gets more compelling if we lift the restrictions on fixed sized buffers as it would allow for Span<T> instances of even greater length.

Se è necessario arrestare questo percorso, è possibile che il linguaggio soddisfi questo problema garantendo che tali Span<T> istanze fossero solo verso il basso.If there is ever a need to go down this path then the language could accommodate this by ensuring such Span<T> instances were downward facing only. Si tratta di una condizione di escape sicura per l'ambito in cui sono stati creati.That is they were only ever safe-to-escape to the scope in which they were created. In questo modo si garantisce che la lingua non debba mai considerare un ref valore di escape per un metodo tramite un valore ref struct restituito o un campo di ref struct .This ensure the language never had to consider a ref value escaping a method via a ref struct return or field of ref struct. Questo potrebbe anche richiedere ulteriori modifiche per riconoscere questi costruttori come l'acquisizione di un ref parametro in questo modo.This would likely also require further changes to recognize such constructors as capturing a ref parameter in this way though.