你可以在MSIL中做什么,你不能在C#或VB.NET中做什么?
所有使用.NET语言编写的代码都会编译为MSIL,但是您可以直接使用MSIL来执行特定的任务/操作吗?
让我们在MSIL上做的事情也比C#,VB.NET,F#,j#或其他任何.NET语言更容易。
到目前为止我们有这个:
- 尾recursion
- 一般Co / Contravariance
- 重载仅在返回types中有所不同
- 覆盖访问修饰符
- 有一个不能从System.Objectinheritance的类
- 过滤的exception(可以在vb.net中完成)
- 调用当前静态类types的虚方法。
- 获取值types的盒装版本的句柄。
- 做一个尝试/错误。
- 禁止使用的名称。
- 为值types定义自己的无参数构造函数 。
- 用一个
raise
元素定义事件。 - CLR允许一些转换,但C#不允许。
- 做一个非
main()
方法作为.entrypoint
。 - 直接使用本地
int
和本地unsigned int
types。 - 玩瞬态指针
- MethodBodyItem中的emitbyte指令
- 抛出并捕获非System.Exceptiontypes
- inheritance枚举(未validation)
- 您可以将一个字节数组作为一个(4x小)的整数数组。
- 你可以有一个字段/方法/属性/事件都具有相同的名称(未validation)。
- 你可以从自己的catch块分支回try块。
- 您可以访问famandassem访问说明符(
protected internal
是fam 或汇编) - 直接访问用于定义全局函数的
<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,必须手动实现。 (抖动也可以自由地进行优化,但这是另一个问题。)
可以将protected
和internal
访问修饰符组合在一起。 在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在虚拟方法调用的call
和callvirt
之间有区别。 通过使用前者,您可以强制调用当前静态类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#语法所要求的。
还有一些:
- 代表中可以有额外的实例方法。
- 代表可以实现接口。
- 您可以在委托和接口中拥有静态成员。
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即可。