你可以在MSIL中做什么,你不能在C#或VB.NET中做什么?

所有使用.NET语言编写的代码都会编译为MSIL,但是您可以直接使用MSIL来执行特定的任务/操作吗?

让我们在MSIL上做的事情也比C#,VB.NET,F#,j#或其他任何.NET语言更容易。

到目前为止我们有这个:

  1. 尾recursion
  2. 一般Co / Contravariance
  3. 重载仅在返回types中有所不同
  4. 覆盖访问修饰符
  5. 有一个不能从System.Objectinheritance的类
  6. 过滤的exception(可以在vb.net中完成)
  7. 调用当前静态类types的虚方法。
  8. 获取值types的盒装版本的句柄。
  9. 做一个尝试/错误。
  10. 禁止使用的名称。
  11. 为值types定义自己的无参数构造函数 。
  12. 用一个raise元素定义事件。
  13. CLR允许一些转换,但C#不允许。
  14. 做一个非main()方法作为.entrypoint
  15. 直接使用本地int和本地unsigned inttypes。
  16. 玩瞬态指针
  17. MethodBodyItem中的emitbyte指令
  18. 抛出并捕获非System.Exceptiontypes
  19. inheritance枚举(未validation)
  20. 您可以将一个字节数组作为一个(4x小)的整数数组。
  21. 你可以有一个字段/方法/属性/事件都具有相同的名称(未validation)。
  22. 你可以从自己的catch块分支回try块。
  23. 您可以访问famandassem访问说明符( protected internal是fam 汇编)
  24. 直接访问用于定义全局函数的<Module>类或模块初始值设定项。

MSIL允许重载仅因返回types而不同

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

要么

 callvirt int32 ... 

包括C#和VB在内的大多数.Net语言都不使用MSIL代码的尾recursionfunction。

尾recursion是在函数式语言中常见的优化。 当方法A通过返回方法B的值而结束时,方法A的堆栈可以在方法B的调用完成后被释放。

MSIL代码显式支持尾recursion,对于一些algorithm,这可能是一个重要的优化。 但是由于C#和VB不会生成指令来执行此操作,因此必须手动完成(或使用F#或其他语言)。

下面是一个如何在C#中手动实现尾recursion的例子:

 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; } } 

通过将本地数据从硬件堆栈移动到堆栈分配的堆栈数据结构来消除recursion是常见的做法。 在如上所示的尾调用recursion消除中,堆栈被完全消除,这是一个相当不错的优化。 而且,返回值并不需要漫长的调用链,而是直接返回。

但是,无论如何,CIL提供这个function作为语言的一部分,但是使用C#或VB,必须手动实现。 (抖动也可以自由地进行优化,但这是另一个问题。)

可以将protectedinternal访问修饰符组合在一起。 在C#中,如果您编写protected internal成员是从程序集和派生类访问。 通过MSIL你可以得到一个只能在程序集内派生类访问的成员。 (我认为这可能是非常有用的!)

在MSIL中,可以有一个不能从System.Objectinheritance的类。

示例代码:使用ilasm.exe进行编译UPDATE:必须使用“/ NOAUTOINHERIT”来防止汇编程序自动inheritance。

 // 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 

哦,当时我没有发现这一点。 (如果添加jon-skeet标签更有可能,但是我不会经常检查它。)

看起来你已经有了相当不错的答案。 此外:

  • 你不能在C#中的值types的盒装版本。 你可以在C ++ / CLI中
  • 你不能在C#中做一个尝试/错误(“错误”就像是“抓住一切,在块的末尾重新抛出”或“最后但只有失败”)
  • 有很多C#禁止的名字,但合法的IL
  • IL允许您为值types定义自己的无参数构造函数 。
  • 你不能在C#中使用“raise”元素来定义事件。 (在VB中你必须为自定义事件,但“默认”事件不包括一个。)
  • CLR允许一些转换,但C#不允许。 如果你通过C#中的object ,这些有时会工作。 看一个uint [] / int [] SO问题的例子。

如果我想到其他的东西,我会补充一点。

CLR已经支持generic co / contravariance,但C#直到4.0才得到这个function

  • C#4.0特性
  • CO /逆变

在IL中,您可以抛出并捕获任何types的数据,而不仅仅是从System.Exception派生的types。

IL在虚拟方法调用的callcallvirt之间有区别。 通过使用前者,您可以强制调用当前静态类types的虚拟方法,而不是dynamic类types中的虚拟函数。

C#没有办法做到这一点:

 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!"; } } 

像IL一样,VB可以使用MyClass.Method()语法来发出非虚拟调用。 在上面,这将是MyClass.ToString()

在try / catch中,你可以从自己的catch块重新inputtry块。 所以,你可以这样做:

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

AFAIK你不能在C#或VB中做到这一点

使用IL和VB.NET,可以在捕获exception时添加filter,但C#v3不支持此function。

这个VB.NET的例子取自http://blogs.msdn.com/clrteam/archive/2009/02/05/catch-rethrow-and-filters-why-you-should-care.aspx (注意When ShouldCatch(ex) = True Catch子句中的When ShouldCatch(ex) = True ):

 Try Foo() Catch ex As CustomBaseException When ShouldCatch(ex) Console.WriteLine("Caught exception!") End Try 

据我所知,无法直接在C#中创build模块初始值设定项(整个模块的静态构造函数):

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

Native types
你可以直接使用本地int和原生的unsigned inttypes(在C#中,你只能在一个不相同的IntPtr上工作。

Transient Pointers
您可以使用瞬态指针进行游戏,指针指向托pipetypes,但保证不会在内存中移动,因为它们不在托pipe堆中。 不能完全确定如何有用地使用这个,而不会弄乱非托pipe代码,但它不会直接通过像stackalloc这样的东西直接暴露给其他语言。

<Module>
如果你愿意的话,你可以把课程搞得一团糟(你可以通过反思来完成,而不需要IL)

.emitbyte

15.4.1.1 .emitbyte指令MethodBodyItem :: = … | .emitbyte Int32该指令导致无符号的8位值直接发送到方法的CILstream中,在指令出现的位置。 [注:.emitbyte指令用于生成testing。 生成常规程序不是必需的。 尾注]

.entrypoint
你有更多的灵活性,你可以将它应用到不叫做Main的方法。

有一个规范的阅读,我相信你会发现更多。

你可以破解方法覆盖co / contra-variance,这是C#不允许的(这不是一般的方差!)。 这里有更多关于实现这个的信息 ,以及第1部分和第2部分

我认为我所希望的(完全是错误的)是Enums的inheritance。 在SMIL中做这件事似乎并不困难(因为Enums只是类),但这不是C#语法所要求的。

还有一些:

  1. 代表中可以有额外的实例方法。
  2. 代表可以实现接口。
  3. 您可以在委托和接口中拥有静态成员。

20)你可以把一个字节数组看作一个(4x小)的整数数组。

我最近使用这个来做一个快速的XOR实现,因为CLR异或函数在int上运行,我需要在字节stream上执行XOR。

测得的代码比在C#中完成的等效速度要快10倍(在每个字节上执行XOR)。

===

我没有足够的stackoverflow街道credz来编辑问题,并添加到列表#20,如果别人可以,将膨胀;-)

混淆器使用的东西 – 你可以有一个字段/方法/属性/事件都具有相同的名称。

枚举inheritance是不可能的:

你可以inheritance一个Enum类。 但是结果并不像Enum那样。 它的行为不像一个价值types,但像一个普通的类。 奇怪的是:IsEnum:True,IsValueType:True,IsClass:False

但那不是特别有用(除非你想混淆一个人或运行时本身。)

你也可以在IL中从System.Multicast委托中派生一个类,但是你不能在C#中这样做:

//下面的类定义是非法的:

public class YourCustomDelegate:MulticastDelegate {}

您也可以在IL中定义模块级别(又称为全局)方法,而C#则相反,只允许您定义方法,只要它们至less连接到一个types即可。