用.NET语言编写的所有代码都编译为MSIL,但是只有使用MSIL才能直接执行特定的任务/操作吗?
让我们在MSIL中比C#,VB.NET,F#,j#或任何其他.NET语言更容易完成.
到目前为止我们有这个:
尾递归
通用Co/Contravariance
重载仅在返回类型上有所不同
覆盖访问修饰符
有一个不能从System.Object继承的类
过滤的异常(可以在vb.net中完成)
调用当前静态类类型的虚方法.
获取值类型的盒装版本的句柄.
做一个尝试/错误.
使用禁用名称.
为值类型定义自己的无参数构造函数.
使用raise
元素定义事件.
CLR允许一些转换,但C#不允许转换.
做一个非main()
方法作为.entrypoint
.
直接使用本机int
和本机unsigned int
类型.
玩瞬态指针
MethodBodyItem中的emitbyte指令
抛出并捕获非System.Exception类型
继承枚举(未验证)
您可以将字节数组视为(4x更小)整数数组.
您可以使字段/方法/属性/事件具有相同的名称(未验证).
您可以从自己的catch块分支回try块.
您可以访问famandassem访问说明符(protected internal
是fam 或 assem)
直接访问
类以定义全局函数或模块初始值设定项.
Anton Gogole.. 34
MSIL允许重载仅因返回类型而异
call void [mscorlib]System.Console::Write(string)
要么
callvirt int32 ...
如果除了返回类型之外两个方法相同,可以从C#或vb.net调用吗? (12认同)
这太棒了.谁不想让回程超载? (8认同)
你怎么知道这种东西?:) (5认同)
Jeffrey L Wh.. 28
大多数.Net语言(包括C#和VB)都不使用MSIL代码的尾递归功能.
尾递归是一种在函数式语言中很常见的优化.它发生在方法A通过返回方法B的值而结束时,一旦调用方法B,就可以释放方法A的堆栈.
MSIL代码显式支持尾递归,对于某些算法,这可能是一个重要的优化.但由于C#和VB不生成执行此操作的指令,因此必须手动完成(或使用F#或其他语言).
下面是一个如何在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; } }
通常的做法是通过将本地数据从硬件堆栈移动到堆分配的堆栈数据结构来删除递归.在如上所示的尾调用递归消除中,完全消除了堆栈,这是一个非常好的优化.此外,返回值不必走长调用链,但直接返回.
但是,无论如何,CIL提供此功能作为语言的一部分,但使用C#或VB,它必须手动实现.(抖动也可以自由地进行优化,但这是另一个问题.)
MSIL允许重载仅因返回类型而异
call void [mscorlib]System.Console::Write(string)
要么
callvirt int32 ...
大多数.Net语言(包括C#和VB)都不使用MSIL代码的尾递归功能.
尾递归是一种在函数式语言中很常见的优化.它发生在方法A通过返回方法B的值而结束时,一旦调用方法B,就可以释放方法A的堆栈.
MSIL代码显式支持尾递归,对于某些算法,这可能是一个重要的优化.但由于C#和VB不生成执行此操作的指令,因此必须手动完成(或使用F#或其他语言).
下面是一个如何在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; } }
通常的做法是通过将本地数据从硬件堆栈移动到堆分配的堆栈数据结构来删除递归.在如上所示的尾调用递归消除中,完全消除了堆栈,这是一个非常好的优化.此外,返回值不必走长调用链,但直接返回.
但是,无论如何,CIL提供此功能作为语言的一部分,但使用C#或VB,它必须手动实现.(抖动也可以自由地进行优化,但这是另一个问题.)
在MSIL中,您可以拥有一个不能从System.Object继承的类.
示例代码:使用ilasm.exe UPDATE编译它:您必须使用"/ NOAUTOINHERIT"来防止汇编程序自动继承.
// 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
可以组合使用protected
和internal
访问修饰符.在C#中,如果编写protected internal
成员,则可以从程序集和派生类访问.通过MSIL你可以得到一个成员是从组件内的派生类访问只.(我认为这可能非常有用!)
哦,我当时没有发现这一点.(如果你添加jon-skeet标签更有可能,但我不经常检查它.)
看起来你已经有了很好的答案.此外:
您无法处理C#中值类型的盒装版本.您可以在C++/CLI中
你不能在C#中做一个尝试/错误("故障"就像是"抓住一切并在块的末尾重新抛出"或"最后但仅在失败时")
C#禁止使用许多名称但合法的IL
IL允许您为值类型定义自己的无参数构造函数.
您无法在C#中使用"raise"元素定义事件.(在VB中,您必须使用自定义事件,但"默认"事件不包括一个.)
CLR允许一些转换,但C#不允许转换.如果你通过object
C#,这些有时会起作用.有关示例,请参阅uint []/int [] SO问题.
如果我想到别的什么,我会加上这个......
CLR已经支持通用协同/反演,但C#直到4.0才获得此功能
C#4.0功能
CO /逆变
在IL中,你可以抛出并捕获任何类型,而不仅仅是派生类型System.Exception
.
IL具有虚拟方法调用call
和callvirt
虚拟方法调用之间的区别.通过使用前者,您可以强制调用当前静态类类型的虚方法,而不是动态类类型中的虚函数.
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()
.
使用IL和VB.NET,您可以在捕获异常时添加过滤器,但C#v3不支持此功能.
这个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条款):
Try
Foo()
Catch ex As CustomBaseException When ShouldCatch(ex)
Console.WriteLine("Caught exception!")
End Try
在try/catch中,您可以从其自己的catch块重新输入try块.所以,你可以这样做:
.try { // ... MidTry: // ... leave.s RestOfMethod } catch [mscorlib]System.Exception { leave.s MidTry // branching back into try block! } RestOfMethod: // ...
AFAIK你不能用C#或VB做到这一点
据我所知,没有办法直接在C#中创建模块初始化器(整个模块的静态构造函数):
http://blogs.msdn.com/junfeng/archive/2005/11/19/494914.aspx
Native types
您可以直接使用native int和native unsigned int类型(在c#中,您只能使用不同的IntPtr.
Transient Pointers
您可以使用瞬时指针,它是指向托管类型的指针,但保证不会在内存中移动,因为它们不在托管堆中.不完全确定如何在不弄乱非托管代码的情况下如何有效地使用它,但它不会直接通过stackalloc之类的东西直接暴露给其他语言.
如果你愿意的话,你可以搞乱这个课程(你可以通过反思来做到这一点而不需要IL)
.emitbyte
15.4.1.1 .emitbyte指令MethodBodyItem :: = ... | .emitbyte Int32此指令使无符号的8位值直接发送到方法的CIL流中,在指令出现的位置.[注意:.emitbyte指令用于生成测试.生成常规程序不需要它.结束说明]
.entrypoint
您可以在此方面获得更多灵活性,例如,您可以将其应用于未称为Main的方法.
阅读规范,我相信你会发现更多.
你可以破解方法覆盖co/contra-variance,C#不允许(这与通用方差不同!).我在这里有更多关于实现它的信息,以及第1和第2部分