In C# 5, la semantica di chiusura dell'istruzione foreach
(quando la variabile di iterazione è "acquisita" o "chiusa sopra" da funzioni anonime) era famously changed (link to thread on that topic).Semantica di chiusura per foreach su array di tipi di puntatore
Domanda: Era l'intenzione di cambiare questo anche per gli array di tipi di puntatore?
Il motivo per cui mi chiedo è che il "espansione" di una dichiarazione foreach
deve essere riscritta, per motivi tecnici (non possiamo usare la proprietà Current
del System.Collections.IEnumerator
poiché questa proprietà ha dichiarato tipo object
che non è compatibile con un puntatore tipo) rispetto a foreach
rispetto ad altre raccolte. La sezione relativa nel linguaggio C# Specification, di "matrici Pointer", in versione 5.0, dice che:
foreach (V v in x) EMBEDDED-STATEMENT
si espande a:
{
T[,,…,] a = x;
V v;
for (int i0 = a.GetLowerBound(0); i0 <= a.GetUpperBound(0); i0++)
for (int i1 = a.GetLowerBound(1); i1 <= a.GetUpperBound(1); i1++)
…
for (int in = a.GetLowerBound(N); iN <= a.GetUpperBound(n); iN++) {
v = (V)a.GetValue(i0,i1,…,iN);
EMBEDDED-STATEMENT
}
}
Notiamo che la dichiarazione V v;
è al di fuori di tutti i loop for
. Quindi sembrerebbe che la semantica di chiusura sia ancora il "vecchio" sapore di C# 4, "la variabile di loop viene riutilizzata, la variabile di ciclo è" esterna "rispetto al ciclo".
per far capire di cosa sto parlando, si consideri questo completo programma C# 5:
using System;
using System.Collections.Generic;
static class Program
{
unsafe static void Main()
{
char* zeroCharPointer = null;
char*[] arrayOfPointers =
{ zeroCharPointer, zeroCharPointer + 1, zeroCharPointer + 2, zeroCharPointer + 100, };
var list = new List<Action>();
// foreach through pointer array, capture each foreach variable 'pointer' in a lambda
foreach (var pointer in arrayOfPointers)
list.Add(() => Console.WriteLine("Pointer address is {0:X2}.", (long)pointer));
Console.WriteLine("List complete");
// invoke those delegates
foreach (var act in list)
act();
}
// Possible output:
//
// List complete
// Pointer address is 00.
// Pointer address is 02.
// Pointer address is 04.
// Pointer address is C8.
//
// Or:
//
// List complete
// Pointer address is C8.
// Pointer address is C8.
// Pointer address is C8.
// Pointer address is C8.
}
Allora, qual è il corretto output del programma di cui sopra?
Nota: È possibile che l'espansione ha un altro problema evidente in quanto scrive 'a.GetValue (I0, I1, ..., in)' 'dove GetValue' sembra essere il metodo definito da' Sistema .Array'. Ma quel metodo ha valore di ritorno 'oggetto', quindi non può essere usato per i tipi di puntatore. Quindi la specifica C# non riesce ad evitare _ "qualsiasi tentativo di accedere agli elementi dell'array tramite' System.Array' "_, per citare la specifica C# stessa. Forse avrebbe dovuto essere "a [i0, i1, ..., iN]" dove la parentesi '[...]' è definita dalla sottosezione _ "Array element access" _. Prova a dire 'arrayOfPointers.GetValue (0)' da solo, nell'esempio di codice precedente. –