2009-03-15 7 views
43

Ilekroć przydzielić nową tablicę w języku C# zbezpośrednie inicjalizacji tablicy ze stałą wartością

new T[length] 

wpisy tablicy są ustawione na domyślne T. To znaczy null dla przypadku, T jest typem odniesienia lub wynik domyślnego konstruktora T, jeśli T jest typem wartości.

W moim przypadku chcę zainicjować Int32 tablicę o wartości -1:

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

Więc po pamięci jest zarezerwowana na tablicy, CLR pętle nad nowo przydzielonej pamięci i ustawia wszystkie wpisy do default (int) = 0. Następnie mój kod ustawia wszystkie pozycje na -1.

To powoduje, że inicjalizacja jest zbędna. Czy JIT wykryje to i zaniedbuje inicjalizację do 0, a jeśli nie, czy istnieje sposób bezpośredniego zainicjowania części pamięci o wartości niestandardowej?

Odwołanie się do C# Array initialization - with non-default value, używanie Enumerable.Repeat(value, length).ToArray() nie jest opcją, ponieważ Enumerable.ToArray przydziela nową tablicę i kopiuje do niej później wartości.

+1

Jeśli masz bajt array, następnie [P/Invoke mógłby pomóc] (http://stackoverflow.com/a/19060558/380331). Ale jeśli rozmiar elementu tablicy jest większy niż bajt (jak w twoim przypadku) - ta metoda nie pomoże. –

Odpowiedz

32

To nie jest zbyteczne.

Załóżmy, że podczas pętli inicjalizacyjnej zostanie zgłoszony wyjątek. Jeśli CLR nie wyczyścił najpierw pamięci, możesz "zobaczyć" oryginalną niezainicjowaną pamięć, co jest bardzo złym pomysłem, szczególnie z punktu widzenia bezpieczeństwa. Właśnie dlatego CLR gwarantuje, że każda nowo przydzielona pamięć zostanie wyczyszczona do schematu 0 bitowego.

Ten sam argument dotyczy pól w obiekcie, nawiasem mówiąc.

Przypuszczam, że w obu przypadkach CLR może sprawdzić, że nie zamierzasz uczynić tablicy widoczną gdzie indziej przed zakończeniem inicjalizacji, ale jest to skomplikowana kontrola, aby uniknąć całkiem prostego "wyczyszczenia tego obszaru pamięci".

+0

Nie widzę, gdzie można rzucić wyjątek. JIT jest wystarczająco inteligentny, aby wyłączyć sprawdzanie zasięgu tablicy w pętli, aby mógł wykryć, że nie może wystąpić żaden wyjątek. – Rauhotz

+6

@Rauhotz ThreadAbortException może być wyrzucany w dowolnym momencie. – JaredPar

+0

OK, to jest punkt – Rauhotz

3

Mam wielką wątpliwość, czy JIT zoptymalizuje domyślny zestaw dla tego scenariusza. Powodem jest to, że byłaby to zauważalna różnica. Rozważmy następujący nieznacznie zmieniony scenariusz.

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

Pętla może być w całości dostępna. Przynajmniej JIT prawdopodobnie nie jest w stanie udowodnić, że tak nie jest. Jeśli wyrzucił, a CLR nie zainicjował domyślnie pamięci, wynik byłby możliwy do zaobserwowania, gdyby nadal miałeś odniesienie do obiektu.

+0

Ogranicz problem do zmiennych lokalnych. – Rauhotz

+0

@Rauhotz, w tym momencie musisz zacząć zastanawiać się, czy optymalizacja jest tego warta. Teraz ograniczyłeś to do bardzo ograniczonego scenariusza. – JaredPar

10

Jeśli kupisz w Arrays considered somewhat harmful, wówczas sprawa byłaby dyskusyjna, jak można napisać:

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

Prawdopodobnie warto wspomnieć o jego linku do bloga Erica Lipperta, który twierdzi, że tablice są złymi wiadomościami, ponieważ w wielu przypadkach "dzwoniący żąda wartości, a kelner wypełnia żądanie, przekazując zmienne". Lippert następnie interesująco argumentuje, że z powodu ograniczeń fizycznych tworzących szybsze procesory, "Będziemy potrzebować języków programowania, które pozwolą zwykłym śmiertelnikom pisać kod, który można zrównoważyć z wieloma rdzeniami [i tymi] tablicami ... zdecydowanie przeciwdziała temu celowi. " Jest to świetna lektura, jeśli nie jest to tylko tangencjalnie związana z tym pytaniem. ; ^) – ruffin

26

podobne do odpowiedzi Dana ale bez konieczności stosowania kolekcje:

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