可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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:
- Tail recursion
- Generic Co/Contravariance
- Overloads which differ only in return types
- Override access modifiers
- Have a class which cannot inherit from System.Object
- Filtered exceptions (can be done in vb.net)
- Calling a virtual method of the current static class type.
- Get a handle on the boxed version of a value type.
- Do a try/fault.
- Usage of forbidden names.
- Define your own parameterless constructors for value types.
- Define events with a
raise
element.
- Some conversions allowed by the CLR but not by C#.
- Make a non
main()
method as the .entrypoint
.
- work with the native
int
and native unsigned int
types directly.
- Play with transient pointers
- emitbyte directive in MethodBodyItem
- Throw and catch non System.Exception types
- Inherit Enums (Unverified)
- You can treat an array of bytes as a (4x smaller) array of ints.
- You can have a field/method/property/event all have the same name(Unverified).
- You can branch back into a try block from its own catch block.
- You have access to the famandassem access specifier (
protected internal
is famorassem)
- Direct access to the
<Module>
class for defining global functions, or a module initializer.
回答1:
MSIL allows for overloads which differ only in return types because of
call void [mscorlib]System.Console::Write(string)
or
callvirt int32 ...
回答2:
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.)
回答3:
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!)
回答4:
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
回答5:
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...
回答6:
The CLR supports generic co/contravariance already, but C# is not getting this feature until 4.0
- C# 4.0 Features
- Co/Contravariance
回答7:
In IL you can throw and catch any type at all, not just types derived from System.Exception
.
回答8:
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()
.
回答9:
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
回答10:
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
回答11:
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
回答12:
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.
回答13:
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
回答14:
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.
回答15:
Here's some more:
- You can have extra instance methods in delegates.
- Delegates can implement interfaces.
- You can have static members in delegates and interfaces.
回答16:
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 ;-)
回答17:
Something obfuscators use - you can have a field/method/property/event all have the same name.
回答18:
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.)
回答19:
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
{
}
回答20:
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.