Domanda

Ho trovato quanto segue piuttosto strano. Inoltre, ho usato principalmente chiusure in linguaggi dinamici che non dovrebbero essere sospettabili con lo stesso "bug". Quanto segue rende il compilatore infelice:

VoidFunction t = delegate { int i = 0; };

int i = 1;

Dice:

  

Una variabile locale denominata 'i' non può essere   dichiarato in questo ambito perché   darebbe un significato diverso a "i",   che è già utilizzato in un "bambino"   possibilità di indicare qualcos'altro

Quindi, questo significa sostanzialmente che le variabili dichiarate all'interno di un delegato avranno l'ambito della funzione dichiarata. Non esattamente quello che mi sarei aspettato. Non ho nemmeno provato a chiamare la funzione. Almeno Common Lisp ha una funzione in cui dici che una variabile dovrebbe avere un nome dinamico, se vuoi davvero che sia locale. Ciò è particolarmente importante quando si creano macro che non perdono, ma qualcosa del genere sarebbe utile anche qui.

Quindi mi chiedo cosa fanno gli altri per aggirare questo problema?

Per chiarire sto cercando una soluzione in cui le variabili che dichiaro nel delegato non interferiscano con le variabili dichiarate dopo il delegato. E voglio essere ancora in grado di catturare le variabili dichiarate prima del delegato.

È stato utile?

Soluzione

Deve essere in questo modo consentire ai metodi anonimi (e lambdas) di utilizzare variabili e parametri locali nell'ambito del metodo contenitore.

Le soluzioni alternative devono utilizzare nomi diversi per la variabile o creare un metodo normale.

Altri suggerimenti

La "chiusura" creato da una funzione anonima è leggermente diverso da quello creato in altri linguaggi dinamici (userò Javascript come esempio).

function thing() {
    var o1 = {n:1}
    var o2 = {dummy:"Hello"}
    return function() { return o1.n++; }
}

var fn = thing();
alert(fn());
alert(fn());

Questo piccolo pezzo di javascript visualizzerà 1 quindi 2. La funzione anonima può accedere alla variabile o1 perché esiste nella sua catena di portata. Tuttavia, la funzione anonima ha un ambito completamente indipendente in cui potrebbe creare un'altra variabile o1 e quindi nasconderne altre lungo la catena dell'ambito. Si noti inoltre che restano tutte le variabili nell'intera catena, quindi o2 continuerebbe a esistere tenendo un riferimento a oggetto fino a quando fn varialbe contiene il riferimento a funzione.

Ora confronta con le funzioni anonime di C #: -

class C1 { public int n {get; set;} }
class C2 { public string dummy { get; set; } }

Func<int> thing() {
   var o1 = new C1() {n=1};
   var o2 = new C2() {dummy="Hello"};
   return delegate { return o1.n++; };
}
...
Func<int> fn = thing();
Console.WriteLine(fn());
Console.WriteLine(fn());

In questo caso la funzione anonima non sta creando un ambito veramente indipendente più di quanto non sarebbe la dichiarazione di variabile in qualsiasi altro {} blocco di codice in funzione (utilizzato in un foreach , if , ecc.)

Quindi si applicano le stesse regole, il codice esterno al blocco non può accedere alle variabili dichiarate all'interno del blocco ma non è nemmeno possibile riutilizzare un identificatore.

Viene creata una chiusura quando la funzione anonima viene passata al di fuori della funzione in cui è stata creata. La variazione dall'esempio Javascript è che rimarranno solo quelle variabili effettivamente utilizzate dalla funzione anonima, quindi in questo caso l'oggetto possedeva di o2 sarà disponibile per GC non appena la cosa sarà completata,

Riceverai anche CS0136 da codice come questo:

  int i = 0;
  if (i == 0) {
    int i = 1;
  }

L'ambito di applicazione della seconda dichiarazione di "i" è inequivocabile, i linguaggi come il C ++ non hanno alcuna importanza. Ma i progettisti del linguaggio C # hanno deciso di vietarlo. Dato lo snippet di cui sopra, pensi ancora che sia stata una cattiva idea? Inserisci un sacco di codice extra e potresti fissare questo codice per un po 'e non vedere il bug.

La soluzione alternativa è banale e indolore, basta trovare un nome di variabile diverso.

È perché il delegato può fare riferimento a variabili esterne al delegato:

int i = 1;
VoidFunction t = delegate { Console.WriteLine(i); };

Se ricordo bene, il compilatore crea un membro della classe delle variabili esterne a cui fa riferimento il metodo anonimo, al fine di farlo funzionare.

Ecco una soluzione alternativa:

class Program
    {
        void Main()
        {
            VoidFunction t = RealFunction;
            int i = 1;
        }
        delegate void VoidFunction();
        void RealFunction() { int i = 0; }
    } 

In realtà, l'errore non sembra avere nulla a che fare con delegati anonimi o espressioni lamda. Se provi a compilare il seguente programma ...

using System;

class Program
{
    static void Main()
    {
        // Action t = delegate
        {
            int i = 0;
        };

        int i = 1;
    }
}

... ricevi esattamente lo stesso errore, indipendentemente dal fatto che tu commenti nella riga o meno. La guida all'errore mostra un caso molto simile. Penso che sia ragionevole non consentire entrambi i casi in quanto i programmatori potrebbero confondere le due variabili.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top