Podstawowym problemem związanym z „void” jest to, że nie oznacza to tego samego, co każdy inny typ zwrotu. „void” oznacza „jeśli ta metoda zwróci, to nie zwróci żadnej wartości”. Nie jest zerem; null jest wartością. Nie zwraca żadnej wartości.
To naprawdę psuje system typów. System typów jest zasadniczo systemem do dokonywania logicznych dedukcji dotyczących tego, jakie operacje są poprawne dla poszczególnych wartości; nieważna metoda zwracająca wartość nie zwraca wartości, więc pytanie „jakie operacje są poprawne w tej sprawie?” nie ma żadnego sensu. Nie ma „rzeczy”, aby istniała operacja, ważna lub nieprawidłowa.
Co więcej, to zepsuło środowisko uruchomieniowe coś gwałtownego. Środowisko wykonawcze .NET jest implementacją wirtualnego systemu wykonawczego, który jest określony jako maszyna stosowa. Oznacza to maszynę wirtualną, w której wszystkie operacje są scharakteryzowane pod względem ich wpływu na stos oceny. (Oczywiście w praktyce maszyna zostanie zaimplementowana na maszynie ze stosem i rejestrami, ale wirtualny system wykonawczy przyjmuje tylko stos.) Efekt wywołania metody void jest zasadniczoinny niż efekt wywołania metody innej niż void; metoda nie void zawsze kładzie coś na stosie, co może wymagać oderwania. Metoda void nigdy nie kładzie czegoś na stosie. Dlatego kompilator nie może traktować metod void i non-void tak samo w przypadku, gdy zwracana wartość metody jest ignorowana; jeśli metoda jest nieważna, nie ma wartości zwracanej, więc nie może być pop.
Z tych wszystkich powodów „void” nie jest typem, który można utworzyć; nie ma żadnych wartości , o to właśnie chodzi. Nie można go przekształcić w obiekt, a metody zwracania pustek nigdy nie można nigdy traktować polimorficznie za pomocą metody nie zwracającej pustki, ponieważ powoduje to uszkodzenie stosu!
Dlatego void nie może być użyty jako argument typu, co jest wstydem, jak zauważasz. Byłoby to bardzo wygodne.
Z perspektywy czasu byłoby lepiej dla wszystkich zainteresowanych, gdyby zamiast niczego metoda zwracania pustek automatycznie zwracała „Unit”, magiczny typ odniesienia singletonu. Wiedziałbyś wtedy, że każde wywołanie metody umieszcza coś na stosie , wiedziałbyś, że każde wywołanie metody zwraca coś, co można przypisać do zmiennej typu obiektu , i oczywiście Unit może być użyty jako argument typu , więc byłoby nie trzeba mieć osobnych typów delegatów Action i Func. Niestety, nie w tym świecie się znajdujemy.
Aby uzyskać więcej przemyśleń w tym duchu, zobacz: