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

时间:2009-02-12 15:55:34

标签: c# .net clr cil

所有用.NET语言编写的代码都编译成MSIL,但是你是否只能直接使用MSIL执行特定的任务/操作?

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

到目前为止,我们有这个:

  1. 尾递归
  2. Generic Co / Contravariance
  3. 仅在返回类型方面不同的重载
  4. 覆盖访问修饰符
  5. 有一个不能从System.Object继承的类
  6. 过滤的异常(可以在vb.net中完成)
  7. 调用当前静态类类型的虚方法。
  8. 获取值类型的盒装版本。
  9. 尝试/错误。
  10. 使用禁用名称。
  11. Define your own parameterless constructors for value types
  12. 使用raise元素定义事件。
  13. CLR允许的某些转换,但C#不允许转换。
  14. 制作非main()方法作为.entrypoint
  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>类以定义全局函数或模块初始值设定项。

20 个答案:

答案 0 :(得分:34)

MSIL允许由于

而仅在返回类型方面不同的重载
call void [mscorlib]System.Console::Write(string)

callvirt int32 ...

答案 1 :(得分: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,它必须手动实现。 (抖动也可以自由地进行优化,但这是另一个问题。)

答案 2 :(得分: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

答案 3 :(得分:19)

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

答案 4 :(得分:18)

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

看起来你已经有了很好的答案。另外:

  • 您无法处理C#中值类型的盒装版本。您可以在C ++ / CLI
  • 你不能在C#中做一个尝试/错误(“错误”就像是“抓住所有东西并在街区尽头重新抛出”或“最后但仅在失败时”)
  • C#禁止使用许多名称但IL合法
  • IL允许您define your own parameterless constructors for value types
  • 您无法在C#中使用“raise”元素定义事件。 (在VB中,您拥有来进行自定义事件,但“默认”事件不包含一个。)
  • CLR允许某些转换,但C#不允许转换。如果你通过C#中的object,这些有时会有效。有关示例,请参阅uint[]/int[] SO question

如果我想到其他任何事情,我会加上这个...

答案 5 :(得分:17)

CLR已经支持通用协同/反演,但C#直到4.0才获得此功能

答案 6 :(得分:14)

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

答案 7 :(得分:10)

对于虚方法调用,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()

答案 8 :(得分:9)

使用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(请注意Catch子句中的 When ShouldCatch(ex) = True ):

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

答案 9 :(得分:9)

在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

来做这件事

答案 10 :(得分:8)

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

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

答案 11 :(得分:7)

Native types
您可以直接使用native int和native unsigned int类型(在c#中,您只能使用不同的IntPtr。

Transient Pointers
您可以使用瞬时指针,它是指向托管类型的指针,但保证不会在内存中移动,因为它们不在托管堆中。不完全确定如何在不弄乱非托管代码的情况下有效地使用它,但它不会直接通过stackalloc之类的东西直接暴露给其他语言。

<Module>
如果你愿意,你可以搞乱这个课程(你可以通过反思来做到这一点而不需要IL)

.emitbyte

  

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

.entrypoint
您可以在此方面获得更多灵活性,例如,可以将其应用于未称为Main的方法。

阅读spec我确信你会再找到一些。

答案 12 :(得分:6)

你可以破解C#不允许的方法覆盖co / contra-variance(这与通用方差不同!)。我已经获得了有关实施此here和部分12

的更多信息

答案 13 :(得分:4)

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

答案 14 :(得分:4)

还有一些:

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

答案 15 :(得分:3)

20)您可以将字节数组视为(4x更小)整数数组。

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

测得的结果代码比C#中的等效代码快10倍(在每个字节上执行XOR)。

===

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

答案 16 :(得分:3)

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

答案 17 :(得分:2)

Enum继承实际上是不可能的:

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

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

答案 18 :(得分:2)

您也可以从IL中的System.Multicast委托派生一个类,但是您不能在C#中执行此操作:

  

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

     

公共类YourCustomDelegate:   MulticastDelegate   {   }

答案 19 :(得分:1)

您还可以在IL中定义模块级(也称为全局)方法,而C#只允许您定义方法,只要它们附加到至少一种类型。