2009-03-15 2 views
43

Quando si assegna un nuovo array in C# coninizializzazione campo diretto con un valore costante

new T[length] 

gli elementi dell'array sono impostati sul valore predefinito T. Cioè null per il caso che T è un tipo di riferimento o il risultato del costruttore predefinito di T, se T è un tipo di valore.

Nel mio caso voglio inizializzare un array Int32 con il valore -1:

var myArray = new int[100]; 
for (int i=0; i<myArray.Length; i++) { myArray[i] = -1; } 

Così, dopo la memoria è riservata per la matrice, il CLR loop sopra la memoria appena allocata e imposta tutte le voci default (int) = 0. Dopodiché, il mio codice imposta tutte le voci su -1.

Ciò rende l'inizializzazione ridondante. Il JIT lo rileva e trascura l'inizializzazione a 0 e in caso contrario, c'è un modo per inizializzare direttamente una porzione di memoria con un valore personalizzato?

In riferimento a C# Array initialization - with non-default value, l'utilizzo di Enumerable.Repeat(value, length).ToArray() non è un'opzione, poiché Enumerable.ToArray alloca un nuovo array e copia i valori in esso successivamente.

+1

Se si dispone di byte array, quindi [P/Invoke potrebbe aiutare] (http://stackoverflow.com/a/19060558/380331). Ma se la dimensione dell'elemento dell'array è maggiore del byte (come nel tuo caso), questo metodo non sarà di aiuto. –

risposta

32

Non è ridondante.

Supponiamo che venga generata un'eccezione durante il ciclo di inizializzazione. Se il CLR non ha eliminato prima la memoria, potresti essere in grado di "vedere" la memoria originale non inizializzata, che è una pessima idea, in particolare dal punto di vista della sicurezza. Questo è il motivo per cui il CLR garantisce che qualsiasi memoria appena allocata venga cancellata con un pattern a 0 bit.

Lo stesso argomento vale per i campi in un oggetto, tra l'altro.

Suppongo che in entrambi i casi il CLR potrebbe verificare che non si renderà visibile la matrice altrove prima di terminare l'inizializzazione, ma è un controllo complicato per evitare una semplice "cancellazione di quest'area di memoria".

+0

Non vedo dove potrebbe essere generata un'eccezione. Il JIT è abbastanza intelligente da disabilitare i controlli di intervallo sull'array all'interno del ciclo, in modo che possa anche rilevare che non può verificarsi alcuna eccezione. – Rauhotz

+6

@Rauhotz ThreadAbortException può essere lanciato in qualsiasi momento. – JaredPar

+0

OK, questo è un punto – Rauhotz

3

Dubito fortemente che il JIT ottimizzerà il set predefinito per questo scenario. La ragione è che questa sarebbe una differenza osservabile. Si consideri il seguente scenario leggermente modificato.

obj.myArray = new int[100]; 
for (int i=0; i<myArray.Length; i++) { obj.myArray[i] = -1; } 

È del tutto possibile che il ciclo venga lanciato. Almeno, probabilmente non è possibile per il JIT dimostrare che non lo è. Se ha gettato e il CLR non ha inizializzato la memoria di default, il risultato sarebbe osservabile se avessi ancora un riferimento a obj.

+0

Limita il problema alle variabili locali. – Rauhotz

+0

@Rauhotz, a quel punto devi iniziare a chiedersi se valga la pena o meno l'ottimizzazione. Ora lo hai limitato a uno scenario molto limitato. – JaredPar

10

Se si acquista in Arrays considered somewhat harmful, allora la vostra domanda sarebbe discutibile come si può scrivere:

var myArray = new List<int>(Enumerable.Repeat(-1, 100)); 
+4

Probabilmente vale la pena menzionare il suo collegamento al blog di Eric Lippert, il che dimostra che gli array sono cattive notizie perché, in molti casi, "il chiamante chiede valori. Il destinatario soddisfa la richiesta restituendo le variabili". Lippert sostiene quindi che, a causa dei vincoli fisici che rendono i processori più veloci, "avremo bisogno di linguaggi di programmazione che consentano ai comuni mortali di scrivere codice che sia parallelizzabile a più core [e che] array ... che vadano contro questo obiettivo. " È una buona lettura, se a malapena legata a questa domanda. ; ^) – ruffin

26

Simile alla risposta di Dan ma senza la necessità di utilizzare collezioni:

int[] myArray = Enumerable.Repeat(-1, 100).ToArray();