Condividi tramite


"uso basato su modelli" e "dichiarazioni using"

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 language design meeting (LDM) pertinenti.

Altre informazioni sul processo di adozione delle speclette di funzionalità nello standard del linguaggio C# sono disponibili nell'articolo sulle specifiche .

Problema del campione: https://github.com/dotnet/csharplang/issues/114

Sommario

Il linguaggio aggiungerà due nuove funzionalità relative all'istruzione using per semplificare la gestione delle risorse: using deve riconoscere un modello eliminabile oltre a IDisposable e aggiungere una dichiarazione di using al linguaggio.

Motivazione

La dichiarazione using è uno strumento efficace per la gestione delle risorse oggi, ma richiede un po ' di cerimonia. I metodi che hanno un certo numero di risorse da gestire possono incagliarsi sintatticamente con una serie di istruzioni using. Questo onere sintattico è tale che la maggior parte delle linee guida di stile di codifica prevede esplicitamente un'eccezione per le parentesi graffe in questo scenario.

La dichiarazione di using rimuove molta della complessità qui e porta C# al livello di altri linguaggi che includono blocchi di gestione delle risorse. Inoltre, la using basata su modelli consente agli sviluppatori di espandere il set di tipi che possono partecipare qui. In molti casi, si elimina la necessità di creare tipi wrapper che esistono solo per consentire l'utilizzo di valori in un'istruzione using.

Insieme queste funzionalità consentono agli sviluppatori di semplificare ed espandere gli scenari in cui è possibile applicare using.

Progettazione dettagliata

dichiarazione using

La lingua consentirà di aggiungere using a una dichiarazione di variabile locale. Tale dichiarazione avrà lo stesso effetto della dichiarazione della variabile in un'istruzione using nella stessa posizione.

if (...) 
{ 
   using FileStream f = new FileStream(@"C:\source\using.md");
   // statements
}

// Equivalent to 
if (...) 
{ 
   using (FileStream f = new FileStream(@"C:\source\using.md")) 
   {
    // statements
   }
}

La durata di un using locale si estenderà fino alla fine del contesto in cui viene dichiarato. I using locali verranno quindi eliminati nell'ordine inverso in cui sono dichiarati.

{ 
    using var f1 = new FileStream("...");
    using var f2 = new FileStream("...");
    using var f3 = new FileStream("...");
    ...
    // Dispose f3
    // Dispose f2 
    // Dispose f1
}

Non esistono restrizioni per gotoo per qualsiasi altro costrutto di flusso di controllo in presenza di una dichiarazione di using. Al contrario, il codice agisce esattamente come per l'istruzione using equivalente:

{
    using var f1 = new FileStream("...");
  target:
    using var f2 = new FileStream("...");
    if (someCondition) 
    {
        // Causes f2 to be disposed but has no effect on f1
        goto target;
    }
}

Una variabile dichiarata in una dichiarazione locale using sarà implicitamente di sola lettura. Questo corrisponde al comportamento delle variabili locali dichiarate in un'istruzione using.

La grammatica per le dichiarazioni di using sarà la seguente:

local-using-declaration:
  'using' type using-declarators

using-declarators:
  using-declarator
  using-declarators , using-declarator
  
using-declarator:
  identifier = expression

Restrizioni relative alla dichiarazione di using:

  • Potrebbe non apparire direttamente all'interno di un'etichetta case, ma deve trovarsi in un blocco all'interno dell'etichetta case.
  • Non può comparire come parte di una dichiarazione di variabile out.
  • Deve avere un inizializzatore per ogni dichiaratore.
  • Il tipo locale deve essere convertibile in modo implicito in IDisposable o soddisfare il modello di using.

basato su modelli tramite

Il linguaggio aggiungerà il concetto di modello eliminabile per i tipi ref struct, ovvero un ref struct che ha un metodo di istanza Dispose accessibile. I tipi che soddisfano il modello eliminabile possono partecipare a un'istruzione o a una dichiarazione using senza dover implementare IDisposable.

ref struct Resource
{ 
    public void Dispose() { ... }
}

using (var r = new Resource())
{
    // statements
}

In questo modo gli sviluppatori potranno sfruttare using per i tipi di ref struct. Questi tipi non possono implementare interfacce in C# 8 e quindi non possono partecipare a istruzioni using.

Le stesse restrizioni di un'istruzione using tradizionale si applicano anche qui: le variabili locali dichiarate nel using sono di sola lettura, un valore null non causerà la generazione di un'eccezione e così via... La generazione del codice sarà diversa solo perché non verrà eseguito un cast per IDisposable prima di chiamare Dispose:

{
	  Resource r = new Resource();
	  try {
		    // statements
	  }
	  finally {
		    if (r != null) r.Dispose();
	  }
}

Per adattare il modello eliminabile, il metodo Dispose deve essere un membro dell'istanza accessibile, senza parametri, e avere un tipo di ritorno void. Non può essere un metodo di estensione.

Considerazioni

Nessuna di queste considerazioni è stata implementata in C# 8

etichette case senza blocchi

Un using declaration è illegale direttamente all'interno di un'etichetta case a causa di complicazioni legate alla sua durata effettiva. Una possibile soluzione consiste semplicemente nel dare ad esso la stessa durata di un out var nella stessa posizione. È stato ritenuto che la complessità aggiuntiva per l'implementazione della funzionalità e la facilità della soluzione alternativa (aggiungere solo un blocco all'etichetta case) non giustificassero l'adozione di questo percorso.

Espansioni Future

variabili locali fisse

Un'istruzione fixed ha tutte le proprietà delle istruzioni using che hanno portato alla possibilità di avere variabili locali using. È consigliabile considerare l'estensione di questa funzionalità anche a fixed locali. Le regole relative alla durata e all'ordinamento dovrebbero essere applicate equamente per using e fixed qui.