172

All code written in .NET languages compiles to MSIL, but are there specific tasks / operations that you can do only using MSIL directly?

Let us also have things done easier in MSIL than C#, VB.NET, F#, j# or any other .NET language.

So far we have this:

  1. Tail recursion
  2. Generic Co/Contravariance (allowed in C# 4 and VB 10)
  3. Overloads which differ only in return types
  4. Override access modifiers
  5. Have a class which cannot inherit from System.Object
  6. Filtered exceptions (allowed in VB, and C# 6)
  7. Calling a virtual method of the current static class type.
  8. Get a handle on the boxed version of a value type.
  9. Do a try/fault.
  10. Usage of forbidden names.
  11. Define your own parameterless constructors for value types.
  12. Define events with a raise element.
  13. Some conversions allowed by the CLR but not by C#.
  14. Make a non main() method as the .entrypoint.
  15. work with the native int and native unsigned int types directly.
  16. Play with transient pointers
  17. emitbyte directive in MethodBodyItem
  18. Throw and catch non System.Exception types
  19. Inherit Enums (Unverified)
  20. You can treat an array of bytes as a (4x smaller) array of ints.
  21. You can have a field/method/property/event all have the same name(Unverified).
  22. You can branch back into a try block from its own catch block.
  23. You have access to the famandassem access specifier (protected internal is famorassem, but now allowed in C# 7.2 and VB 15.5)
  24. Direct access to the <Module> class for defining global functions, or a module initializer.
  25. Create and use non-zero-bound 1-based arrays
  26. Create open-instance and closed-static delegates, and delegates of getters/setters
  27. Swap two values without using a temp variable
  28. Explicit interface implementation with any name, and implementing two interface functions in one (can be done in VB)
  29. Declaring vtfixup (the equivalent of extern in C)
  30. Specifying arbitrary modopt or modreq
Charlieface
  • 52,284
  • 6
  • 19
  • 43
Binoj Antony
  • 15,886
  • 25
  • 88
  • 96

20 Answers20

35

MSIL allows for overloads which differ only in return types because of

call void [mscorlib]System.Console::Write(string)

or

callvirt int32 ...
Anton Gogolev
  • 113,561
  • 39
  • 200
  • 288
30

Most .Net languages including C# and VB do not use the tail recursion feature of MSIL code.

Tail recursion is an optimization that is common in functional languages. It occurs when a method A ends by returning the value of method B such that method A's stack can be deallocated once the call to method B is made.

MSIL code supports tail recursion explicitly, and for some algorithms this could be a important optimization to make. But since C# and VB do not generate the instructions to do this, it must be done manually (or using F# or some other language).

Here is an example of how tail-recursion may be implemented manually in C#:

private static int RecursiveMethod(int myParameter)
{
    // Body of recursive method
    if (BaseCase(details))
        return result;
    // ...

    return RecursiveMethod(modifiedParameter);
}

// Is transformed into:

private static int RecursiveMethod(int myParameter)
{
    while (true)
    {
        // Body of recursive method
        if (BaseCase(details))
            return result;
        // ...

        myParameter = modifiedParameter;
    }
}

It is common practice to remove recursion by moving the local data from the hardware stack onto a heap-allocated stack data structure. In the tail-call recursion elimination as shown above, the stack is eliminated completely, which is a pretty good optimization. Also, the return value does not have to walk up a long call-chain, but it is returned directly.

But, anyway, the CIL provides this feature as part of the language, but with C# or VB it has to be implemented manually. (The jitter is also free to make this optimization on its own, but that is a whole other issue.)

Jeffrey L Whitledge
  • 58,241
  • 9
  • 71
  • 99
  • 1
    F# does not use MSIL's tail recursion, because it only works in fully trusted (CAS) cases because of the way it does not leave a stack to check for permission assrtions (etc.). – Richard Feb 22 '09 at 21:54
  • 11
    Richard, I'm not sure what you mean. F# certainly does emit the tail. call prefix, pretty much all over the place. Examine the IL for this: "let print x = print_any x". – MichaelGG Feb 24 '09 at 23:22
  • 1
    I believe that the JIT will use tail recursion anyway in some cases - and in some cases will ignore the explicit request for it. It depends on the processor architecture, IIRC. – Jon Skeet Feb 26 '09 at 16:20
  • @Jon Skeet, processor architecture is irrelevant for implementing tail recursion (I.e., processor-agnostic languages like Eiffel is known to optimize code this way, as does Saxon's XSLT2-SA, with Java byte code). But yes, a JIT could be optimized to do so, regardless of processor, and it does happen with .NET's JIT. – Abel Jul 31 '10 at 22:18
  • 3
    @Abel: While processor architecture is irrelevant in *theory*, it's not irrelevant in *practice* as the different JITs for different architectures have different rules for tail recursion in .NET. In other words, you could *very easily* have a program which blew up on x86 but not on x64. Just because tail recursion *can* be implemented in both cases doesn't mean it *is*. Note that this question is about .NET specifically. – Jon Skeet Jul 31 '10 at 22:33
  • @Jon Skeet, yes, this way it makes sense. Sorry for misunderstanding you earlier. You never sleep, do you? ;) – Abel Jul 31 '10 at 23:06
  • 2
    C# actually does tail calls under x64 in specific cases: http://community.bartdesmet.net/blogs/bart/archive/2010/07/07/the-case-of-the-failed-demo-stackoverflowexception-on-x64.aspx. – Pieter van Ginkel Nov 01 '10 at 20:11
  • @Pieter - I don't believe the MSIL code generated by the C# compiler will be any different between x86 and x64. I think the article that you linked referrs to how the MSIL code is JIT compiled. Since C# and VB are still not generating the .tail instruction decoration, this is still an example of something available in MSIL that is not available in C# or VB. In C#, there is currently no way to even hint to the MSIL generator or JIT compiler that the tail recursion optimization should be made. It’s a good thing the x64 JITer is able to figure it out by itself, so we don't have to muck with MSIL! – Jeffrey L Whitledge Nov 01 '10 at 20:44
  • Gosh, think you're right. Read the article a few months ago and apparently remembered it incorrectly. Well, this still stands then :). – Pieter van Ginkel Nov 01 '10 at 21:50
  • @JeffreyLWhitledge: I would much rather have a language include a `tail return` statement which would either yield a "tail return" instruction or refuse compilation when that would not be possible, than have a compiler try to auto-generate tail returns. If a piece of code would require use of tail return in order to run correctly, it should be impossible for that code to compile without using tail return. And if tail return isn't necessary, it would generally accomplish nothing. – supercat Oct 14 '14 at 22:30
22

In MSIL, you can have a class which cannot inherit from System.Object.

Sample code: compile it with ilasm.exe UPDATE: You must use "/NOAUTOINHERIT" to prevent assembler from auto inheriting.

// Metadata version: v2.0.50215
.assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                         // .z\V.4..
  .ver 2:0:0:0
}
.assembly sample
{
  .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) 
  .hash algorithm 0x00008004
  .ver 0:0:0:0
}
.module sample.exe
// MVID: {A224F460-A049-4A03-9E71-80A36DBBBCD3}
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003       // WINDOWS_CUI
.corflags 0x00000001    //  ILONLY
// Image base: 0x02F20000


// =============== CLASS MEMBERS DECLARATION ===================

.class public auto ansi beforefieldinit Hello
{
  .method public hidebysig static void  Main(string[] args) cil managed
  {
    .entrypoint
    // Code size       13 (0xd)
    .maxstack  8
    IL_0000:  nop
    IL_0001:  ldstr      "Hello World!"
    IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_000b:  nop
    IL_000c:  ret
  } // end of method Hello::Main
} // end of class Hello
Ramesh
  • 13,043
  • 3
  • 52
  • 88
  • That doesn't *explicitly* inherit from System.Object, but it does it implicitly, I believe. See ECMA 335 section 8.9.9 - it has to *at least* indirectly inherit from System.Object. – Jon Skeet Feb 26 '09 at 16:20
  • If you compile, and then disassemble, you'll see: .class public auto ansi beforefieldinit Hello extends [mscorlib]System.Object – Michael Trausch Feb 26 '09 at 18:04
  • @www.trausch.us - you need to compile using ilasm code.il /NOAUTOINHERIT – Ramesh Feb 26 '09 at 20:05
  • @Ramesh That is non-portable; that option does not exist in (for example) the Mono 2.0 version of ilasm which follows ECMA as cited by Jon Skeet strictly. – Michael Trausch Feb 26 '09 at 20:26
  • 3
    @Jon Skeet - With all due respect, Could you please help me understand what NOAUTOINHERIT mean. MSDN specifies "Disables default inheritance from Object when no base class is specified.New in the .NET Framework version 2.0." – Ramesh Feb 26 '09 at 20:48
  • 3
    @Michael - The question is regarding MSIL and not Common Intermediate Language. I agree this may not be possible in CIL but, it still works with MSIL – Ramesh Feb 26 '09 at 20:52
  • 5
    @Ramesh: Oops, you're absolutely right. I'd say at that point it breaks the standard spec, and shouldn't be used. Reflector doesn't even load the assmebly. However, it *can* be done with ilasm. I wonder why on earth it's there. – Jon Skeet Feb 26 '09 at 21:04
  • 4
    (Ah, I see the /noautoinherit bit was added after my comment. At least I feel somewhat better about not realising it before...) – Jon Skeet Feb 26 '09 at 21:06
  • @Jon - Yes I added after seeing, Michael comment. I would modify the answer to reflect I have updated it. – Ramesh Feb 26 '09 at 21:09
  • @Ramesh: Sorry, I wasn't trying to criticise you for not making the update more obviously an update earlier on. I was just relieved that I hadn't missed anything glaringly obvious or failed to read your question the first time. I still wonder what the point is though... – Jon Skeet Feb 26 '09 at 23:35
  • @Ramesh / @Jon Skeet / @all: FWIW the line in Ecma-335 that defines and prohibits this is: *"Every Class (with the exception of `System.Object` and the special class ``) shall extend one, and only one, other Class – so `Extends` for a Class shall be non-null [ERROR]"* (the word error here means: if this rule is not obeyed, an error must be raised). I think that pretty much sums it up *why* this cannot work. – Abel Jul 31 '10 at 22:26
  • 1
    I'll add that at least on .NET 4.5.2 on Windows it compile but doesn't execute (`TypeLoadException`). PEVerify returns: *[MD]: Error: TypeDef that is not an Interface and not the Object class extends Nil token.* – xanatos Apr 02 '15 at 08:52
21

It's possible to combine the protected and internal access modifiers. In C#, if you write protected internal a member is accessible from the assembly and from derived classes. Via MSIL you can get a member which is accessible from derived classes within the assembly only. (I think that could be pretty useful!)

yatima2975
  • 6,580
  • 21
  • 42
  • 5
    It is a candidate now to be implemented in C# 7.1 (https://github.com/dotnet/csharplang/issues/37), the access modifier is `private protected` – Happypig375 Apr 08 '17 at 11:45
  • 5
    It's been released as part of C# 7.2: https://blogs.msdn.microsoft.com/dotnet/2017/11/15/welcome-to-c-7-2-and-span/ – Joe Sewell Nov 16 '17 at 14:27
19

Ooh, I didn't spot this at the time. (If you add the jon-skeet tag it's more likely, but I don't check it that often.)

It looks like you've got pretty good answers already. In addition:

  • You can't get a handle on the boxed version of a value type in C#. You can in C++/CLI
  • You can't do a try/fault in C# ("fault" is a like a "catch everything and rethrow at the end of the block" or "finally but only on failure")
  • There are lots of names which are forbidden by C# but legal IL
  • IL allows you to define your own parameterless constructors for value types.
  • You can't define events with a "raise" element in C#. (In VB you have to for custom events, but "default" events don't include one.)
  • Some conversions are allowed by the CLR but not by C#. If you go via object in C#, these will sometimes work. See a uint[]/int[] SO question for an example.

I'll add to this if I think of anything else...

Community
  • 1
  • 1
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
18

The CLR supports generic co/contravariance already, but C# is not getting this feature until 4.0

ermau
  • 1,303
  • 7
  • 19
15

In IL you can throw and catch any type at all, not just types derived from System.Exception.

Daniel Earwicker
  • 114,894
  • 38
  • 205
  • 284
  • 6
    You can do that in C# too, with `try`/`catch` without parentheses in the catch-statement you will catch non-Exception-like exceptions too. Throwing, however, is indeed only possible when you inherit from `Exception`. – Abel Jul 31 '10 at 22:40
  • @Abel You can hardly say that you're catching something if you can't refer to it. – Jim Balter Jan 02 '17 at 06:18
  • 2
    @JimBalter if you don't catch it, the application crashes. If you catch it, the application doesn't crash. So referring to the exception object is distinct from catching it. – Daniel Earwicker Jan 02 '17 at 08:51
  • Lol! A distinction between app terminating or continuing is pedantry? Now I think I may have heard everything. – Daniel Earwicker Jan 02 '17 at 18:47
  • 1
    Interestingly enough, the CLR no longer lets you do this. By default it will wrap non-exception objects in [RuntimeWrappedException](https://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.runtimewrappedexception(v=vs.110).aspx). – Jwosty Aug 03 '18 at 16:41
11

IL has the distinction between call and callvirt for virtual method calls. By using the former you can force calling a virtual method of the current static class type instead of the virtual function in the dynamic class type.

C# has no way of doing this:

abstract class Foo {
    public void F() {
        Console.WriteLine(ToString()); // Always a virtual call!
    }

    public override string ToString() { System.Diagnostics.Debug.Assert(false); }
};

sealed class Bar : Foo {
    public override string ToString() { return "I'm called!"; }
}

VB, like IL, can issue nonvirtual calls by using the MyClass.Method() syntax. In the above, this would be MyClass.ToString().

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
10

In a try/catch, you can re-enter the try block from its own catch block. So, you can do this:

.try {
    // ...

  MidTry:
    // ...

    leave.s RestOfMethod
}
catch [mscorlib]System.Exception {
    leave.s MidTry  // branching back into try block!
}

RestOfMethod:
    // ...

AFAIK you can't do this in C# or VB

thecoop
  • 45,220
  • 19
  • 132
  • 189
  • 3
    I can see why this was omitted - It has a distinct smell of `GOTO` – Basic Jul 04 '12 at 22:14
  • 3
    Sounds much like [On Error Resume Next](https://msdn.microsoft.com/en-us/library/5hsw66as.aspx) in VB.NET – Thomas Weller Jul 22 '15 at 12:37
  • 1
    This can actually be done in VB.NET. [The GoTo statement can branch from `Catch` to its `Try`](https://msdn.microsoft.com/en-us/library/69whc95c.aspx). Run test code online [**here**](https://tio.run/nexus/visual-basic-net-mono#XY8xC8JADIXn5FdkVAStq@AgtUjBmyw41za1gfZO2ivUX19z183pPRLe95LFuHrqmAw@pheZUizCVXoSuoyUW89vHuhMRwSEYviikVrlhACps6PreP8cxPNdLG9kq@O80awGEipaVhhANosnpavPbE15o0ZoF6Gxa/7rOiQIaemrljiusrnijxcXaDdXOFqPwIiLBjDY0BF0fWlZfg). – mbomb007 Feb 09 '17 at 18:58
9

As far as I know, there's no way to make module initializers (static constructors for an entire module) directly in C#:

http://blogs.msdn.com/junfeng/archive/2005/11/19/494914.aspx

yoyoyoyosef
  • 7,000
  • 8
  • 40
  • 39
  • +1 spot on! A great miss in C#, as [I noticed here](http://stackoverflow.com/questions/3363569/what-is-the-earliest-entrypoint-that-the-clr-calls-before-calling-any-method-in-a/3363783#3363783). – Abel Jul 31 '10 at 23:05
9

With IL and VB.NET you can add filters when catching exceptions, but C# v3 does not support this feature.

This VB.NET example is taken from http://blogs.msdn.com/clrteam/archive/2009/02/05/catch-rethrow-and-filters-why-you-should-care.aspx (note the When ShouldCatch(ex) = True in the Catch clause):

Try
   Foo()
Catch ex As CustomBaseException When ShouldCatch(ex)
   Console.WriteLine("Caught exception!")
End Try
Ry-
  • 218,210
  • 55
  • 464
  • 476
Emanuele Aina
  • 376
  • 2
  • 6
  • 17
    Please rRemove the `= True`, it's making my eyes bleed! – Konrad Rudolph Feb 23 '09 at 16:16
  • Why? This is VB and not C#, so no =/== problems exist. ;-) – peSHIr Feb 27 '09 at 09:43
  • Well c# can do "throw;", so the same result can be achieved. – Frank Schwieterman Mar 13 '09 at 16:58
  • 8
    paSHIr, I believe he was talking about the redudancy of it – LegendLength May 06 '09 at 12:36
  • 5
    @Frank Schwieterman: There is a difference between catching and rethrowing an exception, versus holding off on catching it. Filters run before any nested "finally" statements, so the circumstances which caused the exception will still exist when the filter is run. If one is expecting to a significant number of SocketException to be thrown that one will want to catch relatively silently, but a few of them will signal trouble, being able to examine the state when a problematic one is thrown can be very useful. – supercat Jul 01 '11 at 17:57
8

Native types
You can work with the native int and native unsigned int types directly (in c# you can only work on an IntPtr which is not the same.

Transient Pointers
You can play with transient pointers, which are pointers to managed types but guaranteed not to move in memory since they are not in the managed heap. Not entirely sure how you could usefully use this without messing with unmanaged code but it's not exposed to the other languages directly only through things like stackalloc.

<Module>
you can mess about with the class if you so desire (you can do this by reflection without needing IL)

.emitbyte

15.4.1.1 The .emitbyte directive MethodBodyItem ::= … | .emitbyte Int32 This directive causes an unsigned 8-bit value to be emitted directly into the CIL stream of the method, at the point at which the directive appears. [Note: The .emitbyte directive is used for generating tests. It is not required in generating regular programs. end note]

.entrypoint
You have a bit more flexibility on this, you can apply it to methods not called Main for example.

have a read of the spec I'm sure you'll find a few more.

ShuggyCoUk
  • 36,004
  • 6
  • 77
  • 101
  • +1, some hidden gems here. Note that `` is meant as special class for languages that accept global methods (like VB does), but indeed, C# cannot access it directly. – Abel Jul 31 '10 at 23:01
  • Transient pointers seem like they'd be a very useful type; many of the objections to mutable structures stem from their omission. For example, "DictOfPoints(key).X = 5;" would be workable if DictOfPoints(key) returned a transient pointer to a struct, rather than copying the struct by value. – supercat Jun 29 '11 at 13:23
  • @supercat that wouldn't work with a transient pointer, the data in question could be on the heap. what you want is the ref returns the Eric talks about here: http://blogs.msdn.com/b/ericlippert/archive/2011/06/23/ref-returns-and-ref-locals.aspx – ShuggyCoUk Jun 30 '11 at 08:18
  • @ShuggyCoUk: Interesting. I really hope Eric can be persuaded to provide a means by which "DictOfPoints(key).X = 5;" could be made to work. Right now if one wants to hard-code DictOfPoints to work exclusively with type Point (or some other particular type), one can almost make that work, but it's a pain. BTW, one thing that I'd like to see would be a means by which one could write an open-ended generic function e.g. DoStuff<...>(someParams, ActionByRef, ...) which could expand as needed. I'd guess there'd be some way to do that in MSIL; with some compiler help... – supercat Jul 01 '11 at 00:44
  • @ShuggyCoUk: ...it would provide another way of having by-reference properties, with the added bonus that some property could run after everything that outsiders will do to the reference has been done. – supercat Jul 01 '11 at 00:45
  • @supercat Mutable structs are pretty evil, I really wouldn't want to push in a language feature that encouraged it... – ShuggyCoUk Jul 01 '11 at 08:50
  • @ShuggyCoUk: I strenuously disagree. Present language implementations of value structures are evil because they sometimes substitute copies of things for the originals without telling anyone (e.g. when passing a readonly value type by reference, the compiler will silently make a copy and pass that, rather than simply forbidding the operation entirely). I would posit that passing value types by reference is often a better programming paradigm than passing reference types by value; the latter, IMHO, is far more evil. – supercat Jul 01 '11 at 13:02
  • @ShuggyCoUk: The problem with reference types is that there's no limit to where they can go. By contrast, references to value types have clear scope. A class which holds a value type and exposes it via by-reference access method can know it's not going to change outside that method, and (if property references were handled as I'd like to see them) would be guaranteed to be notified when the entity changing them was done. – supercat Jul 01 '11 at 13:22
  • @supercat passing value types by reference is fine if it is within stack lifetime, outside that it gets very complex, since you are potentially significantly altering the lifespan in complex ways. I admit you can do some cool things with it, I'm just not sure it should be in c# the language. One key thing is it massively complicates the type system (you can't box them to object, you can't use them in generic types, you have loads more options to check in reflective scenarios etc.) I just don't think they justify the additional costs to the other parts of the language and framework. – ShuggyCoUk Jul 01 '11 at 18:47
  • @supercat To someone that has a compelling use case my reasoning is not so applicable I admit. – ShuggyCoUk Jul 01 '11 at 18:47
  • @ShuggyCoUk let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/1068/discussion-between-supercat-and-shuggycouk) – supercat Jul 01 '11 at 19:10
6

You can hack method override co/contra-variance, which C# doesn't allow (this is NOT the same as generic variance!). I've got more information on implementing this here, and parts 1 and 2

thecoop
  • 45,220
  • 19
  • 132
  • 189
4

20) You can treat an array of bytes as a (4x smaller) array of ints.

I used this recently to do a fast XOR implementation, since the CLR xor function operates on ints and I needed to do XOR on a byte stream.

The resulting code measured to be ~10x faster than the equivalent done in C# (doing XOR on each byte).

===

I don't have enough stackoverflow street credz to edit the question and add this to the list as #20, if someone else could that would be swell ;-)

rosenfield
  • 159
  • 1
  • 2
  • 8
  • 3
    Rather than dip into IL, you could have accomplished this with unsafe pointers. I'd imagine it would have been just as fast, and perhaps faster, since it would do no bounds checking. – P Daddy Oct 17 '09 at 00:25
4

I think the one I kept wishing for (with entirely the wrong reasons) was inheritance in Enums. It doesn't seem like a hard thing to do in SMIL (since Enums are just classes) but it's not something the C# syntax wants you to do.

Shalom Craimer
  • 20,659
  • 8
  • 70
  • 106
4

Here's some more:

  1. You can have extra instance methods in delegates.
  2. Delegates can implement interfaces.
  3. You can have static members in delegates and interfaces.
leppie
  • 115,091
  • 17
  • 196
  • 297
3

Something obfuscators use - you can have a field/method/property/event all have the same name.

Jason Haley
  • 3,770
  • 18
  • 22
  • 1
    I put a sample out on my site: http://jasonhaley.com/files/NameTestA.zip In that zip there is the IL and an exe that contains a class with the following all the same 'A': -class name is A -Event named A -Method named A -Property named A -2 Fields named A I can't find a good reference to point you at, though I probably read it in either the ecma 335 spec or Serge Lidin's book. – Jason Haley Oct 22 '09 at 13:28
2

Enum inheritance is not really possible:

You can inherit from an Enum class. But the result doesn't behave like an Enum in particular. It behaves not even like a value type, but like an ordinary class. The srange thing is: IsEnum:True, IsValueType:True, IsClass:False

But thats not particulary useful (unless you want to confuse a person or the runtime itself.)

Zotta
  • 2,513
  • 1
  • 21
  • 27
2

You can also derive a class from System.Multicast delegate in IL, but you can't do this in C#:

// The following class definition is illegal:

public class YourCustomDelegate : MulticastDelegate { }

Community
  • 1
  • 1
plaureano
  • 3,139
  • 6
  • 30
  • 29
2

You can also define module-level (aka global) methods in IL, and C#, in contrast, only allows you to define methods as long as they are attached to at least one type.

plaureano
  • 3,139
  • 6
  • 30
  • 29