Condividi tramite


parametri ref readonly

Nota

Questo articolo è una specifica di 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 pertinenti del language design meeting (LDM) .

Puoi trovare ulteriori informazioni sul processo di adozione degli speclet nello standard del linguaggio di programmazione C# leggendo l'articolo sulle specifiche di .

Problema principale: https://github.com/dotnet/csharplang/issues/6010

Sommario

Consenti il modificatore sul sito di dichiarazione del parametro ref readonly e modifica le regole del sito di chiamata come segue:

Annotazione al punto di chiamata parametro ref parametro ref readonly parametro in parametro out
ref Permesso consentiti Avviso Errore
in Errore consentiti Permesso Errore
out Errore errore Errore Permesso
Nessuna annotazione Errore Avvertimento Permesso Errore

Si noti che c'è una modifica alle regole esistenti: il parametro in con l'annotazione "callsite" ref genera un avviso anziché un errore.

Modificare le regole del valore dell'argomento come indicato di seguito:

Tipo di valore parametro ref parametro ref readonly parametro in parametro out
rvalue Errore Avviso Permesso Errore
lvalue Permesso consentiti Permesso Permesso

Dove lvalue indica una variabile (ad esempio, un valore con una posizione; non deve essere scrivibile/assegnabile) e rvalue significa qualsiasi tipo di valore.

Motivazione

C# 7.2 ha introdotto in parametri per trasmettere riferimenti di sola lettura. in parametri consentono sia lvalue che rvalue e possono essere usati senza alcuna annotazione nel callsite. Tuttavia, le API che acquisiscono o restituiscono riferimenti dai loro parametri desiderano impedire l'uso di rvalues e imporre anche un'indicazione al punto di chiamata che un riferimento viene acquisito. ref readonly parametri sono ideali in tali casi in quanto avvertono se usati con rvalue o senza alcuna annotazione al punto di chiamata.

Sono inoltre disponibili API che richiedono solo riferimenti di sola lettura, ma che usano

  • ref parametri sin da quando sono stati introdotti prima che in diventassero disponibili, e la modifica a in comporterebbe un'interruzione a livello di codice sorgente e binario, ad esempio QueryInterface, o
  • in parametri per accettare riferimenti di sola lettura anche se il passaggio di rvalue a tali parametri non ha senso, ad esempio ReadOnlySpan<T>..ctor(in T value)o
  • ref parametri per impedire rvalue anche se non modificano il riferimento passato, ad esempio Unsafe.IsNullRef.

Queste API possono eseguire la migrazione a parametri ref readonly senza interrompere gli utenti. Per informazioni dettagliate sulla compatibilità binaria, vedere la codifica dei metadati proposta. In particolare, cambiando

  • refref readonly sarebbe solo una modifica binaria di rottura per i metodi virtuali,
  • refin sarebbe anche una modifica binaria significativa per i metodi virtuali, ma non una modifica che interrompe il codice sorgente (perché le regole cambiano per avvisare solo sugli argomenti ref passati ai parametri in),
  • inref readonly non sarebbe una modifica di rilievo (ma l'assenza di un'annotazione del punto di chiamata o di un valore r genererebbe un avviso),
    • Si noti che si tratta di una modifica che provoca un'interruzione di origine per gli utenti che utilizzano versioni precedenti del compilatore (poiché interpretano i parametri ref readonly come parametri ref, non consentendo in o nessuna annotazione nel callsite) e per le nuove versioni del compilatore con LangVersion <= 11 (per garantire la coerenza con le versioni precedenti del compilatore, verrà generato un errore se i parametri ref readonly non sono supportati a meno che gli argomenti corrispondenti siano passati con il modificatore ref).

Nella direzione opposta, cambiando

  • ref readonlyref sarebbe potenzialmente una modifica che altera la compatibilità del codice sorgente (a meno che non sia stata utilizzata solo l'annotazione del sito di chiamata ref e utilizzati solo riferimenti di sola lettura come argomenti) e una modifica che altera la compatibilità binaria per i metodi virtuali,
  • ref readonlyin non sarebbe una modifica che causa un'interruzione, ma l'annotazione al callsite ref genererebbe un avviso.

Si noti che le regole descritte in precedenza si applicano alle firme dei metodi, ma non alle firme delegate. Ad esempio, la modifica di ref in in in una firma del delegato può essere una breaking change nel codice sorgente (se un utente assegna un metodo con il parametro ref a quel tipo di delegato, ciò genererebbe un errore dopo la modifica dell'API).

Progettazione dettagliata

In generale, le regole per i parametri di ref readonly sono uguali a quelle specificate per i parametri in in la loro proposta, ad eccezione del caso in cui sia stata modificata in modo esplicito in questa proposta.

Dichiarazioni di parametri

Non sono necessarie modifiche alla grammatica. Il modificatore ref readonly sarà consentito per i parametri. Oltre ai metodi normali, ref readonly sarà consentito per i parametri dell'indicizzatore (ad esempio in ma diversamente da ref), ma non consentiti per i parametri dell'operatore (ad esempio ref ma diversamente da in).

I valori dei parametri predefiniti saranno consentiti per i parametri ref readonly con un avviso poiché sono equivalenti al passaggio di rvalue. In questo modo gli autori di API possono modificare in parametri con valori predefiniti in ref readonly parametri senza introdurre una modifica che rompe la compatibilità con il sorgente.

Controlli dei tipi di valore

Si noti che anche se il modificatore di argomenti ref è consentito per i parametri ref readonly, non cambia nulla rispetto ai controlli del tipo di valore, cioè,

  • ref può essere usato solo con valori assegnabili;
  • per passare riferimenti readonly, è necessario usare invece il modificatore di argomenti in;
  • per passare rvalue, è necessario non usare alcun modificatore (che genera un avviso per i parametri ref readonly come descritto in riepilogo di questa proposta).

Risoluzione dei sovraccarichi

La risoluzione dell'overload consentirà di combinare ref/ref readonly/in/no callite annotazioni e modificatori di parametri indicati dalla tabella in riepilogo di questa proposta, ovvero tutte le consentite e casi di avviso verranno considerati come possibili candidati durante la risoluzione dell'overload. In particolare, esiste una modifica del comportamento esistente in cui i metodi con in parametro corrisponderanno alle chiamate con l'argomento corrispondente contrassegnato come ref. Questa modifica verrà controllata in LangVersion.

Tuttavia, l'avviso per il passaggio di un argomento senza modificatore callsite a un parametro ref readonly verrà soppresso se il parametro è

  • il ricevitore in una chiamata al metodo di estensione
  • usato in modo implicito come parte dell'inizializzatore di raccolta personalizzato o del gestore di stringhe interpolato.

Gli overload per valore saranno preferiti rispetto agli overload ref readonly nel caso in cui non sia presente alcun modificatore dell'argomento (i parametriin hanno lo stesso comportamento).

Conversioni di metodi

Analogamente, ai fini della funzione anonima [§10.7] e del gruppo di metodi [§10.8] le conversioni, questi modificatori sono considerati compatibili (ma qualsiasi conversione consentita tra modificatori diversi genera un avviso):

  • Il parametro ref readonly del metodo di destinazione può corrispondere al parametro in o al parametro ref del delegato.
  • Il parametro in del metodo di destinazione può corrispondere al parametro ref readonly o, in base alla versione del linguaggio, al parametro ref del delegato.
  • Nota: ref parametro del metodo di destinazione non è consentito corrispondere a inref readonly parametro del delegato.

Per esempio:

DIn dIn = (ref int p) => { }; // error: cannot match `ref` to `in`
DRef dRef = (in int p) => { }; // warning: mismatch between `in` and `ref`
DRR dRR = (ref int p) => { }; // error: cannot match `ref` to `ref readonly`
dRR = (in int p) => { }; // warning: mismatch between `in` and `ref readonly`
dIn = (ref readonly int p) => { }; // warning: mismatch between `ref readonly` and `in`
dRef = (ref readonly int p) => { }; // warning: mismatch between `ref readonly` and `ref`
delegate void DIn(in int p);
delegate void DRef(ref int p);
delegate void DRR(ref readonly int p);

Si noti che non sono state apportate modifiche al comportamento delle conversioni dei puntatori a funzione . Come promemoria, le conversioni implicite del puntatore di funzione non sono consentite se esiste una mancata corrispondenza tra i modificatori del tipo di riferimento e i cast espliciti sono sempre consentiti senza avvisi.

Corrispondenza della firma

I membri dichiarati in un singolo tipo non possono differire in firma solo per ref/out/in/ref readonly. Per altri scopi di abbinamento delle firme (ad esempio, nascondere o sovrascrivere), il modificatore ref readonly può essere interscambiato con in, ma ciò genera un avviso nel sito di dichiarazione [§7.6]. Ciò non si applica quando si abbina la dichiarazione di partial alla relativa implementazione e quando si abbina la firma dell'intercettore con la firma intercettata. Si noti che non esiste alcuna modifica nell'override per le coppie di modifica ref/in e ref readonly/ref, non possono essere interscambiate, perché le firme non sono compatibili con i file binari. Per coerenza, lo stesso vale per altri scopi di confronto delle firme (ad esempio, per nascondere).

Codifica dei metadati

Come promemoria,

  • I parametri ref vengono emessi come tipi byref semplici (T& in IL).
  • in parametri sono simili a ref più vengono annotati con System.Runtime.CompilerServices.IsReadOnlyAttribute. In C# 7.3 e versioni successive vengono generati anche con [in] e, se virtuale, modreq(System.Runtime.InteropServices.InAttribute).

ref readonly parametri verranno emessi come [in] T&e annotati con il seguente attributo:

namespace System.Runtime.CompilerServices
{
    [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
    public sealed class RequiresLocationAttribute : Attribute
    {
    }
}

Inoltre, se virtuali, verranno emessi con modreq(System.Runtime.InteropServices.InAttribute) per garantire la compatibilità binaria con i parametri in. Si noti che a differenza dei parametri in, non verrà generato alcun [IsReadOnly] per i parametri ref readonly per evitare di aumentare le dimensioni dei metadati e anche per rendere le versioni meno recenti del compilatore interpretare i parametri ref readonly come parametri ref (e di conseguenza refref readonly non sarà una modifica che causa un'interruzione di origine anche tra versioni diverse del compilatore).

Il RequiresLocationAttribute verrà confrontato con il nome completo dello spazio dei nomi e sintetizzato dal compilatore, se non è già incluso nella compilazione.

Se si specifica l'attributo nell'origine, verrà generato un errore se viene applicato a un parametro, in modo analogo a ParamArrayAttribute.

Puntatori a funzione

Nei puntatori a funzione i parametri in vengono generati con modreq(System.Runtime.InteropServices.InAttribute) (vedere proposta di puntatori a funzione). ref readonly parametri verranno generati senza modreq, ma con modopt(System.Runtime.CompilerServices.RequiresLocationAttribute). Le versioni precedenti del compilatore ignoreranno il modopt e quindi interpreteranno i parametri ref readonly come parametri ref (coerenti con il comportamento del compilatore meno recente per i metodi normali con parametri ref readonly come descritto in precedenza) e le nuove versioni del compilatore che conoscono il modopt lo useranno per riconoscere i parametri ref readonly per generare avvisi durante le conversioni e chiamate. Per coerenza con le versioni precedenti del compilatore, le nuove versioni del compilatore con LangVersion <= 11 segnaleranno errori quando i parametri ref readonly non sono supportati, a meno che gli argomenti corrispondenti non vengano forniti con il modificatore ref.

Si noti che si tratta di un'interruzione binaria per modificare i modificatori nelle firme del puntatore di funzione se fanno parte delle API pubbliche, di conseguenza sarà un'interruzione binaria quando si modifica ref o in in ref readonly. Tuttavia, si verificherà un'interruzione della sorgente solo per i chiamanti con LangVersion <= 11 quando si modifica inref readonly (se si richiama il puntatore con il modificatore del sito di chiamata in), in linea con i metodi normali.

Modifiche interrompenti

Il ref/in relax non corrispondente nella risoluzione dell'overload introduce una modifica che causa un'interruzione del comportamento illustrata nell'esempio seguente:

class C
{
    string M(in int i) => "C";
    static void Main()
    {
        int i = 5;
        System.Console.Write(new C().M(ref i));
    }
}
static class E
{
    public static string M(this C c, ref int i) => "E";
}

In C# 11 la chiamata viene associata a E.M, quindi "E" viene stampata. In C# 12, C.M è autorizzato a eseguire l'associazione (con un avviso) e non viene eseguita alcuna ricerca negli ambiti di estensione poiché è disponibile un candidato applicabile, pertanto "C" viene stampato.

C'è anche una modifica del codice sorgente che causa un'interruzione per lo stesso motivo. L'esempio seguente stampa "1" in C# 11, ma non riesce a compilare con un errore di ambiguità in C# 12:

var i = 5;
System.Console.Write(C.M(null, ref i));

interface I1 { }
interface I2 { }
static class C
{
    public static string M(I1 o, ref int x) => "1";
    public static string M(I2 o, in int x) => "2";
}

Gli esempi precedenti illustrano le interruzioni per le chiamate al metodo, ma poiché sono causate da modifiche alla risoluzione dell'overload, possono essere attivate in modo analogo per le conversioni dei metodi.

Alternative

dichiarazioni di parametri

Gli autori di API possono annotare in parametri progettati per accettare solo lvalue con un attributo personalizzato e fornire un analizzatore per contrassegnare utilizzi non corretti. Ciò non consente agli autori di API di modificare le firme delle API esistenti che hanno scelto di usare parametri ref per impedire rvalue. I chiamanti di tali API devono comunque eseguire operazioni aggiuntive per ottenere un ref se hanno accesso solo a una variabile ref readonly. La modifica di queste API da ref a [RequiresLocation] in sarebbe una modifica che interrompe il codice sorgente (e, nel caso di metodi virtuali, anche un'interruzione binaria).

Invece di consentire il modificatore ref readonly, il compilatore potrebbe riconoscere quando un attributo speciale (ad esempio [RequiresLocation]) viene applicato a un parametro. Questo argomento è stato illustrato in LDM 2022-04-25, decidendo che si tratta di una funzionalità del linguaggio, non di un analizzatore, quindi dovrebbe essere simile a una.

controllo tipo valore

Passare lvalue senza modificatori ai parametri ref readonly può essere consentito senza avvisi, in modo analogo ai parametri byref impliciti di C++. Questo argomento è stato discusso in LDM 2022-05-11, notando che la motivazione principale per i parametri ref readonly sono API che acquisiscono o restituiscono riferimenti da questi parametri, quindi l'indicatore di qualche tipo è una buona cosa.

Il passaggio di rvalue a un ref readonly potrebbe essere un errore, non un avviso. Questo è stato inizialmente accettato in LDM 2022-04-25, ma le discussioni di posta elettronica successive hanno attenuato questa decisione poiché altrimenti si perderebbe la possibilità di modificare le API esistenti senza compromettere gli utenti.

in potrebbe essere il modificatore di callsite "naturale" per i parametri ref readonly e l'uso di ref potrebbe generare avvisi. In questo modo si garantisce uno stile di codice coerente e si rende evidente al punto di chiamata che il riferimento è readonly (a differenza di ref). Inizialmente è stato accettato in LDM 2022-04-25. Tuttavia, gli avvisi potrebbero essere un punto di attrito per consentire agli autori di API di passare da ref a ref readonly. Inoltre, in è stato ridefinito come ref readonly + funzionalità di praticità, di conseguenza questo è stato rifiutato in LDM 2022-05-11.

Revisione LDM in sospeso

Nessuna delle opzioni seguenti è stata implementata in C# 12. Essi rimangono potenziali proposte.

dichiarazioni di parametri

È possibile consentire l'ordinamento inverso dei modificatori (readonly ref anziché ref readonly). Ciò sarebbe incoerente con il modo in cui readonly ref restituisce e i campi (l'ordinamento inverso non è consentito o significa rispettivamente qualcosa di diverso) e potrebbe scontrarsi con parametri di sola lettura se implementati in futuro.

I valori dei parametri predefiniti possono essere un errore per i parametri ref readonly.

controlli del tipo di valore

È possibile generare errori anziché avvisi quando si passano rvalue a parametri ref readonly o quando le annotazioni del sito di chiamata e i modificatori di parametri non corrispondono. Analogamente, è possibile usare modreq speciali anziché un attributo per garantire che i parametri ref readonly siano distinti dai parametri in a livello binario. Ciò garantisce garanzie più solide, quindi sarebbe consigliabile per le nuove API, ma impedire l'adozione nelle API di runtime esistenti che non possono introdurre modifiche di rilievo.

I controlli dei tipi di valore possono essere rilassati per consentire il passaggio di riferimenti di sola lettura tramite ref nei parametri in/ref readonly. Questo sarebbe simile al funzionamento delle assegnazioni di riferimento e dei ritorni ref oggi; essi permettono anche il passaggio di riferimenti come readonly utilizzando il modificatore ref sull'espressione di origine. Tuttavia, il ref si trova di solito vicino al punto in cui la destinazione viene dichiarata come ref readonly, quindi è chiaro che stiamo passando un riferimento come sola lettura, a differenza delle invocazioni i cui modificatori di argomento e parametro di solito sono distanti. Inoltre, consentono di solo modificatore ref a differenza degli argomenti che consentono anche in, di conseguenza in e ref sarebbero diventati intercambiabili per gli argomenti o in sarebbero diventati praticamente obsoleti se gli utenti desiderassero rendere coerente il codice (probabilmente userebbero ref ovunque poiché è l'unico modificatore consentito per le assegnazioni di riferimento e i ritorni di riferimento).

risoluzione del sovraccarico

La risoluzione dell'overload, l'override e la conversione potrebbero impedire l'interscambio tra i modificatori ref readonly e in.

La modifica della risoluzione dell'overload per i parametri di in esistenti potrebbe essere presa in modo incondizionato (non considerando LangVersion), ma sarebbe una modifica che causa un'interruzione.

Il richiamo di un metodo di estensione con il ricevitore ref readonly potrebbe generare un avviso: "L'argomento 1 deve essere passato con la parola chiave ref o in", come per le invocazioni non di estensione prive di modificatori (l'utente potrebbe correggere tale avviso trasformando l'invocazione del metodo di estensione in un'invocazione di metodo statico). Lo stesso avviso potrebbe essere segnalato quando si usa l'inizializzatore di raccolta personalizzato o il gestore di stringhe interpolato con il parametro ref readonly, anche se l'utente non poteva aggirarlo.

ref readonly gli overloads potrebbero essere preferiti agli overloads per valore quando non è presente alcun modificatore di callsite o si potrebbe verificare un errore di ambiguità.

conversioni di metodi

È possibile consentire al parametro ref del metodo di destinazione, in modo che corrisponda ai parametri in e ref readonly del delegato. In questo modo gli autori di API possono modificare, ad esempio, ref in in nelle firme delegate senza interrompere gli utenti (in modo coerente con ciò che è consentito per le firme normali dei metodi). Tuttavia, risulterebbe nella seguente violazione delle garanzie readonly con soltanto un avvertimento:

class Program
{
    static readonly int f = 123;
    static void Main()
    {
        var d = (in int x) => { };
        d = (ref int x) => { x = 42; }; // warning: mismatch between `ref` and `in`
        d(f); // changes value of `f` even though it is `readonly`!
        System.Console.WriteLine(f); // prints 42
    }
}

Le conversioni dei puntatori a funzione potrebbero avvisare in caso di mancata corrispondenza ref readonly/ref/in, ma per condizionarlo su LangVersion, sarebbe necessario un notevole investimento nell'implementazione, poiché attualmente le conversioni dei tipi non richiedono l'accesso alla compilazione. Inoltre, anche se il disallineamento è attualmente un errore, è facile per gli utenti aggiungere un cast per accettare il disallineamento se lo desiderano.

codifica dei metadati

È possibile specificare il RequiresLocationAttribute nell'origine, in modo analogo agli attributi In e Out. In alternativa, potrebbe trattarsi di un errore quando applicato in altri contesti rispetto ai soli parametri, in modo analogo all'attributo IsReadOnly; per preservare ulteriormente lo spazio di progettazione.

I parametri del puntatore di funzione ref readonly possono essere generati con combinazioni di modopt/modreq diverse (si noti che "interruzione del codice sorgente" in questa tabella si riferisce ai chiamanti con LangVersion <= 11):

Modificatori Può essere riconosciuto tra compilazioni I compilatori precedenti li vedono come refref readonly inref readonly
modreq(In) modopt(RequiresLocation) in binario, interruzione del codice sorgente interruzione binaria
modreq(In) No in binario, interruzione del codice sorgente Ok
modreq(RequiresLocation) non confermato binario, interruzione del codice sorgente binario, interruzione del sorgente
modopt(RequiresLocation) ref interruzione binaria binario, interruzione del codice sorgente

È possibile generare attributi [RequiresLocation] e [IsReadOnly] per i parametri di ref readonly. Quindi inref readonly non sarebbe una modifica significativa anche per le versioni precedenti del compilatore, ma refref readonly diventerebbe una modifica incompatibile con il codice sorgente per le versioni meno recenti del compilatore (poiché interpretano ref readonly come in, non consentendo i modificatori ref) così come per le nuove versioni del compilatore con LangVersion <= 11 (per garantire coerenza).

È possibile rendere il comportamento per LangVersion <= 11 diverso dal comportamento per le versioni precedenti del compilatore. Ad esempio, potrebbe trattarsi di un errore ogni volta che viene chiamato un parametro ref readonly (anche quando si usa il modificatore ref nel callite) o può essere sempre consentito senza errori.

Cambiamenti dirompenti

Questa proposta suggerisce di accettare una modifica che altera il comportamento perché è raro che si verifichi, è delimitata da LangVersion e gli utenti possono aggirarla chiamando il metodo di estensione in modo esplicito. Invece, potremmo attenuarlo

  • Impedire la mancata corrispondenza tra ref/in (che impedirebbe solo la migrazione a in per le API precedenti che usavano ref perché in non era ancora disponibile),
  • modifica delle regole di risoluzione del sovraccarico per continuare a cercare una corrispondenza migliore (determinata dalle regole di miglioramento specificate di seguito) quando si verifica una mancata corrispondenza del tipo ref introdotta in questa proposta,
    • o in alternativa continuare solo per la mancata corrispondenza di ref vs. in, non con gli altri (ref readonly vs. ref/in/by-value).
Regole di miglioramento

L'esempio seguente genera attualmente tre errori di ambiguità per le tre chiamate di M. È possibile aggiungere nuove regole di migliorezza per risolvere le ambiguità. In questo modo si risolverà anche la modifica che causa un'interruzione di origine descritta in precedenza. Un modo sarebbe quello di fare in modo che l'esempio stampi 221 (dove il parametro ref readonly è abbinato all'argomento in, poiché sarebbe un avviso chiamarlo senza modificatore, mentre per il parametro in è consentito).

interface I1 { }
interface I2 { }
class C
{
    static string M(I1 o, in int i) => "1";
    static string M(I2 o, ref readonly int i) => "2";
    static void Main()
    {
        int i = 5;
        System.Console.Write(M(null, ref i));
        System.Console.Write(M(null, in i));
        System.Console.Write(M(null, i));
    }
}

Le nuove regole di miglioramento potrebbero contrassegnare come peggio il parametro il cui argomento potrebbe essere stato passato con un modificatore di argomento diverso per migliorarlo. In altre parole, l'utente deve sempre essere in grado di trasformare un parametro peggiore in un parametro migliore modificando il modificatore di argomento corrispondente. Ad esempio, quando un argomento viene passato da in, un parametro ref readonly è preferibile rispetto a un parametro in perché l'utente potrebbe passare l'argomento per valore per scegliere il parametro in. Questa regola è solo un'estensione della regola di preferenza per valore/in che è in vigore oggi (è l'ultima regola di risoluzione dell'overload e l'intero overload è migliore se uno dei relativi parametri è migliore e nessuno è peggiore del parametro corrispondente di un altro overload).

discussione parametro migliore parametro peggiore
ref/in ref readonly in
ref ref ref readonly/in
per valore per valore/in ref readonly
in in ref

È consigliabile gestire le conversioni dei metodi in modo analogo. L'esempio seguente genera attualmente due errori di ambiguità per le due assegnazioni di delegati. Le nuove regole di preferenza potrebbero preferire un parametro del metodo il cui modificatore di riferimento corrisponda al modificatore di riferimento del parametro del delegato di destinazione corrispondente rispetto a uno che non corrisponde. Di conseguenza, l'esempio seguente stampa 12.

class C
{
    void M(I1 o, ref readonly int x) => System.Console.Write("1");
    void M(I2 o, ref int x) => System.Console.Write("2");
    void Run()
    {
        D1 m1 = this.M;
        D2 m2 = this.M; // currently ambiguous

        var i = 5;
        m1(null, in i);
        m2(null, ref i);
    }
    static void Main() => new C().Run();
}
interface I1 { }
interface I2 { }
class X : I1, I2 { }
delegate void D1(X s, ref readonly int x);
delegate void D2(X s, ref int x);

Riunioni progettuali