2016-01-13 21 views
10

Metody i moduły pobierające wydają się być klasą i dlatego są przydzielane na stercie. Jednak inne typy .NET, takie jak List<T>, specyficznie zwracają moduły wyliczające struct, aby uniknąć niepotrzebnego przydzielania pamięci. Z szybkiego przeglądu postu C# In Depth, nie widzę powodu, dla którego tak nie może być w tym przypadku.Dlaczego moduł wyliczający wygenerowany przez kompilator dla "yield" nie jest strukturą?

Czy brakuje mi czegoś?

+0

Właśnie zdałem sobie sprawę, że ponieważ typem zwracanym jest interfejs ('IEnumerable' lub' IEnumerator'), to i tak będzie on (http://stackoverflow.com/questions/3032750/), czy to prawda? W takim przypadku nie można zmienić metody zwracania jawnie wpisanego modułu wyliczającego (np. [List ] (https://msdn.microsoft.com/en-us/library/b0yss765 (v = vs.110). aspx))? Ponieważ implementuje interfejsy, wszystkie odwołania do kodu powinny być zachowane. (IIRC, działa to, ponieważ 'foreach' jest [wykrywany przez wzorzec] (https://blogs.msdn.microsoft.com/ericlippert/2011/06/30/following-the-pattern/)). – Lazlo

Odpowiedz

8

Servy poprawnie odpowiedział na to pytanie - pytanie odpowiedziałeś sobie w komentarzu:

zdałem sobie sprawę, że od zwracany typ jest interfejs, byłoby dostać zapakowane tak, zgadza się?

Dobrze. Twoje pytanie uzupełniające:

nie można zmienić metody zwracania jawnie wpisanego modułu wyliczającego (jak robi to List<T>)?

Więc Chodzi o to, że użytkownik pisze:

IEnumerable<int> Blah() ... 

i kompilator faktycznie generuje metodę, która zwraca BlahEnumerable który jest struct, który implementuje IEnumerable<int>, ale z odpowiednim GetEnumerator etc metody i właściwości które pozwalają na "dopasowanie do wzorca" funkcji foreach, aby usunąć bokser.

Chociaż jest to prawdopodobny pomysł, istnieją poważne trudności, gdy zaczynasz kłamać na temat metody zwrotu metody. Szczególnie gdy kłamstwo polega na zmianie, czy metoda zwraca struct lub typ referencyjny. Pomyśl o wszystkich błędach:

  • Załóżmy, że metoda jest wirtualna. Jak można go przesłonić? Typ zwracanej metody zastępowania wirtualnego musi być dokładnie zgodny z metodą zastępowaną. (I podobnie dla: metoda zastępuje inną metodę, metoda implementuje metodę interfejsu itd.)

  • Załóżmy, że metoda została wprowadzona do delegata Func<IEnumerable<int>>. Func<T> jest kowariantny w T, ale kowariancja dotyczy tylko argumentów typu referencyjnego.Kod wygląda tak, jakby zwracał wartość IEnumerable<T>, ale w rzeczywistości zwracany jest typ wartości, który nie jest zgodny z kowariancyjnie z IEnumerable<T>, a tylko zgodny z.

  • Załóżmy, że mamy void M<T>(T t) where T : class i dzwonimy pod numer M(Blah()). Spodziewamy się wydedukować, że T to IEnumerable<int>, który przechodzi test sprawdzania ograniczeń, ale typ struktury ma wartość , a nie.

I tak dalej. Szybko kończysz w odcinku Three's Company (chłopiec, z którym się tu umawiam), gdzie małe kłamstwo kończy się ogromną katastrofą. Wszystko po to, aby zaoszczędzić niewielką ilość ciśnienia w zbiorniku. Nie jest tego warte.

Zauważam jednak, że implementacja stworzona przez kompilator powoduje, żeoszczędza na ciśnieniu zbierania w jeden interesujący sposób. najpierw czas czas, który jest wywoływany na zwróconych wyliczalnych, przeliczalnych zwojów sam w sobie do modułu wyliczającego. Drugi raz oczywiście stan jest inny, więc przydziela nowy obiekt. Ponieważ prawdopodobny scenariusz 99,99% jest taki, że dana sekwencja jest wyliczana dokładnie raz, jest to duża oszczędność na presję gromadzenia danych.

+0

Dzięki za dokładną odpowiedź! Nalegam na nacisk na kolekcje, ponieważ pracuję ze strasznym wczesnym Mono runtime non-generational GC, więc patrzę na alokacje * bardzo * dokładnie. – Lazlo

6

Ta klasa będzie używać tylko tylko przez interfejs. Gdyby była to struktura, byłaby w 100% pudełkowa, czyniąc ją wydajniejszą niż używanie klasy.

Nie można nie box to, jak to jest z definicji niemożliwe, aby użyć typu w czasie kompilacji, jak nie istnieje podczas uruchamiania kompilacji kodu.

Podczas pisania niestandardowej implementacji IEnumerator można ujawnić rzeczywisty rodzaj bazowy przed skompilowaniem kodu, umożliwiając jego potencjalnie wykorzystanie bez boksowania.

Powiązane problemy