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



9 Answers

包括C#和VB在内的大多数.NET语言都不使用MSIL代码的尾递归功能。

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

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时,必须手动实施。 (抖动也可以自由地进行优化,但这是另一个问题。)

Question

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

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

到目前为止我们有这样的:

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



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

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中定义模块级别(又称全局)方法,而C#相反,只允许您定义方法,只要它们至少连接到一种类型。




Native types
您可以直接使用本地int和本机unsigned int类型(在c#中,您只能在不相同的IntPtr上工作。

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

<Module>
如果你愿意,你可以在课堂上喋喋不休(你可以通过反射来完成而不需要IL)

.emitbyte

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

.entrypoint
例如,您对此有更多的灵活性,您可以将其应用于不称为Main的方法。

阅读spec我相信你会发现更多。




20)你可以将一个字节数组看作一个(4x小)int数组。

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

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

===

我没有足够的 street credz来编辑问题并将其添加到列表#20,如果其他人可能会膨胀;-)




在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



枚举继承是不可能的:

您可以从Enum类继承。 但结果并不像Enum那样。 它的行为甚至不像一个值类型,而是像一个普通的类。 奇怪的是:IsEnum:True,IsValueType:True,IsClass:False

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




我认为我一直希望的(完全是错误的)是Enums中的继承。 在SMIL中做这件事似乎并不困难(因为Enums只是类),但它不是C#语法希望你做的事情。







Related



Tags

c# c#   .net .net   clr   cil