Cały kod napisany w językach .NET kompiluje się do MSIL, ale czy istnieją określone zadania / operacje, które można wykonać tylko bezpośrednio przy użyciu MSIL?
Zróbmy też rzeczy łatwiejsze w MSIL niż C #, VB.NET, F #, j # czy jakikolwiek inny język .NET.
Jak dotąd mamy to:
- Rekursja ogona
- Ogólna współ / kontrawariancja
- Przeciążenia, które różnią się tylko typami zwracanymi
- Zastąp modyfikatory dostępu
- Mieć klasę, która nie może dziedziczyć po System.Object
- Filtrowane wyjątki (można to zrobić w vb.net)
- Wywołanie metody wirtualnej bieżącego typu klasy statycznej.
- Zapoznaj się z pudełkową wersją typu wartości.
- Spróbuj / błąd.
- Używanie zabronionych nazw.
- Zdefiniuj własne konstruktory bez parametrów dla typów wartości .
- Zdefiniuj zdarzenia za pomocą
raise
elementu. - Niektóre konwersje dozwolone przez środowisko CLR, ale nie przez C #.
- Utwórz
main()
metodę inną niż.entrypoint
. - pracować bezpośrednio z typami natywnymi
int
i natywnymiunsigned int
. - Graj z przejściowymi wskaźnikami
- emitbyte w MethodBodyItem
- Wyrzuć i złap typy inne niż System.Exception
- Dziedzicz wyliczenia (niezweryfikowane)
- Tablicę bajtów można traktować jako (4x mniejszą) tablicę liczb całkowitych.
- Możesz mieć wszystkie pola / metody / właściwości / zdarzenia o tej samej nazwie (niezweryfikowane).
- Możesz rozgałęzić się z powrotem do bloku try z własnego bloku catch.
- Masz dostęp do specyfikatora dostępu famandassem (
protected internal
to fam lub assem) - Bezpośredni dostęp do
<Module>
klasy w celu zdefiniowania funkcji globalnych lub inicjatora modułu.
Odpowiedzi:
MSIL zezwala na przeciążenia, które różnią się tylko typami zwracanymi z powodu
lub
źródło
Większość języków .Net, w tym C # i VB, nie korzysta z funkcji rekurencji ogonowej kodu MSIL.
Rekursja ogona to optymalizacja, która jest powszechna w językach funkcjonalnych. Występuje, gdy metoda A kończy się zwracaniem wartości metody B, tak że stos metody A może zostać cofnięty po wywołaniu metody B.
Kod MSIL wyraźnie obsługuje rekurencję ogonową, a dla niektórych algorytmów może to być ważna optymalizacja do wykonania. Ale ponieważ C # i VB nie generują instrukcji, aby to zrobić, należy to zrobić ręcznie (lub za pomocą F # lub innego języka).
Oto przykład, jak rekurencja ogona może być implementowana ręcznie w C #:
Powszechną praktyką jest usuwanie rekursji przez przenoszenie danych lokalnych ze stosu sprzętowego do struktury danych stosu z alokacją sterty. W eliminacji rekurencji tail-call, jak pokazano powyżej, stos jest całkowicie eliminowany, co jest całkiem dobrą optymalizacją. Ponadto wartość zwracana nie musi przechodzić przez długi łańcuch wywołań, ale jest zwracana bezpośrednio.
W każdym razie CIL udostępnia tę funkcję jako część języka, ale w przypadku C # lub VB musi być ona zaimplementowana ręcznie. (Jitter może również samodzielnie przeprowadzić tę optymalizację, ale to zupełnie inna kwestia).
źródło
W MSIL możesz mieć klasę, która nie może dziedziczyć po System.Object.
Przykładowy kod: skompiluj go za pomocą ilasm.exe AKTUALIZACJA: Musisz użyć "/ NOAUTOINHERIT", aby zapobiec automatycznemu dziedziczeniu asemblera.
źródło
TypeLoadException
). PEVerify zwraca: [MD]: Error: TypeDef, który nie jest interfejsem, a nie klasą Object rozszerza token Nil.Możliwe jest łączenie modyfikatorów
protected
iinternal
dostępu. W C #, jeśli napiszesz,protected internal
element członkowski jest dostępny z zestawu iz klas pochodnych. Via MSIL można uzyskać członka, który jest dostępny z klas pochodnych w zespole tylko . (Myślę, że to może być całkiem przydatne!)źródło
private protected
Och, wtedy tego nie zauważyłem. (Jeśli dodasz tag jon-skeet, jest to bardziej prawdopodobne, ale nie sprawdzam go tak często).
Wygląda na to, że masz już całkiem dobre odpowiedzi. Dodatkowo:
object
C #, te czasami będą działać. Zobacz przykład pytania uint [] / int [] SO .Dodam do tego, jeśli pomyślę o czymś innym ...
źródło
<>a
nazwę w języku C # ...Środowisko CLR obsługuje już ogólną współbieżność / kontrawariancję, ale C # nie otrzymuje tej funkcji do 4,0
źródło
W IL możesz wrzucić i złapać dowolny typ, nie tylko typy pochodzące od
System.Exception
.źródło
try
/catch
bez nawiasów w instrukcji catch, aby złapać również wyjątki inne niż wyjątki. Jednak rzucanie jest rzeczywiście możliwe tylko wtedy, gdy dziedziczysz zException
.IL ma rozróżnienie między
call
icallvirt
dla wywołań metod wirtualnych. Używając tego pierwszego, możesz wymusić wywołanie metody wirtualnej bieżącego typu klasy statycznej zamiast funkcji wirtualnej w typie klasy dynamicznej.C # nie ma na to sposobu:
VB, podobnie jak IL, może wywoływać nie wirtualne wywołania, używając
MyClass.Method()
składni. W powyższym byłoby toMyClass.ToString()
.źródło
W trybie try / catch możesz ponownie wprowadzić blok try z jego własnego bloku catch. Więc możesz to zrobić:
AFAIK, nie możesz tego zrobić w C # lub VB
źródło
GOTO
Catch
do jejTry
. Uruchom kod testowy online tutaj .Dzięki IL i VB.NET można dodawać filtry podczas przechwytywania wyjątków, ale C # v3 nie obsługuje tej funkcji.
Ten przykład VB.NET pochodzi z http://blogs.msdn.com/clrteam/archive/2009/02/05/catch-rethrow-and-filters-why-you-should-care.aspx (zwróć uwagę
When ShouldCatch(ex) = True
na Klauzula połowowa):źródło
= True
to, moje oczy krwawią!O ile wiem, nie ma możliwości tworzenia inicjalizatorów modułów (statycznych konstruktorów dla całego modułu) bezpośrednio w C #:
http://blogs.msdn.com/junfeng/archive/2005/11/19/494914.aspx
źródło
Native types
Możesz bezpośrednio pracować z natywnymi typami int i natywnymi unsigned int (w języku C # można pracować tylko na IntPtr, który nie jest taki sam.
Transient Pointers
Możesz grać z przejściowymi wskaźnikami, które są wskaźnikami zarządzanych typów, ale gwarantuje, że nie będą poruszać się w pamięci, ponieważ nie znajdują się na zarządzanej stercie. Nie jestem do końca pewien, jak pożytecznie można go używać bez mieszania się z niezarządzanym kodem, ale nie jest to bezpośrednio ujawniane w innych językach tylko za pośrednictwem rzeczy takich jak stackalloc.
<Module>
możesz zadzierać z klasą, jeśli chcesz (możesz to zrobić przez refleksję bez konieczności posiadania IL)
.emitbyte
.entrypoint
Masz w tym trochę większą elastyczność, możesz zastosować to na przykład do metod innych niż Main.
przeczytaj specyfikację Jestem pewien, że znajdziesz jeszcze kilka.
źródło
<Module>
jest to specjalna klasa dla języków, które akceptują metody globalne (tak jak robi to VB), ale w rzeczywistości C # nie może uzyskać do niej bezpośredniego dostępu.Możesz zhakować metodę przesłaniającą co / contra-variance, na co C # nie zezwala (to NIE jest to samo, co wariancja ogólna!). Mam więcej informacji na temat implementacji tego tutaj oraz części 1 i 2
źródło
Myślę, że jedynym, o którym marzyłem (z całkowicie niewłaściwych powodów) było dziedziczenie w Enums. Wydaje się, że nie jest to trudne zadanie w SMIL (ponieważ wyliczenia to tylko klasy), ale nie jest to coś, czego wymaga składnia C #.
źródło
Oto więcej:
źródło
20) Tablicę bajtów można traktować jako (4x mniejszą) tablicę liczb całkowitych.
Użyłem tego ostatnio do wykonania szybkiej implementacji XOR, ponieważ funkcja CLR xor działa na ints i musiałem zrobić XOR na strumieniu bajtów.
Wynikowy kod był ~ 10x szybszy niż jego odpowiednik w C # (wykonując XOR na każdym bajcie).
===
Nie mam dość stackoverflow street credz, aby edytować pytanie i dodać to do listy jako # 20, jeśli ktoś inny mógłby to być super ;-)
źródło
Coś używanego przez obfuskatorów - możesz mieć pole / metodę / właściwość / zdarzenie, wszystkie mają tę samą nazwę.
źródło
Dziedziczenie wyliczenia nie jest naprawdę możliwe:
Możesz dziedziczyć z klasy Enum. Ale wynik nie zachowuje się szczególnie jak Enum. Zachowuje się nawet nie jak typ wartości, ale jak zwykła klasa. Rzecz w tym, że: IsEnum: True, IsValueType: True, IsClass: False
Ale to nie jest szczególnie przydatne (chyba że chcesz zmylić osobę lub samo środowisko wykonawcze).
źródło
Możesz również wyprowadzić klasę z delegata System.Multicast w IL, ale nie możesz tego zrobić w C #:
źródło
Możesz również zdefiniować metody na poziomie modułu (aka globalne) w języku IL, a C # z kolei umożliwia definiowanie metod tylko, o ile są one dołączone do co najmniej jednego typu.
źródło