首页 文章

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

提问于
浏览
159

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

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

到目前为止我们有这个:

  • 尾递归

  • 通用Co / Contravariance

  • 重载仅在返回类型上有所不同

  • 覆盖访问修饰符

  • 有一个不能从System.Object继承的类

  • 过滤的异常(可以在vb.net中完成)

  • 调用当前静态类类型的虚方法 .

  • 获取值类型的盒装版本的句柄 .

  • 做一次尝试/错误 .

  • 使用禁用名称 .

  • Define your own parameterless constructors for value types .

  • 使用 raise 元素定义事件 .

  • CLR允许一些转换,但C#不允许转换 .

  • main() 方法设为 .entrypoint .

  • 直接使用本机 int 和本机 unsigned int 类型 .

  • 使用瞬态指针播放
    MethodBodyItem中的

  • emitbyte指令

  • 抛出并捕获非System.Exception类型

  • 继承枚举(未验证)

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

  • 您可以让字段/方法/属性/事件具有相同的名称(未验证) .

  • 您可以从自己的catch块分支回try块 .

  • 您可以访问famandassem访问说明符( protected internal 是fam or assem)

  • 直接访问 <Module> 类以定义全局函数或模块初始值设定项 .

20 回答

  • 2

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

    看起来你已经有了很好的答案 . 此外:

    • 您无法处理C#中值类型的盒装版本 . 你可以在C / CLI中

    • 你不能在C#中尝试/错误("fault"就像一个"catch everything and rethrow at the end of the block"或"finally but only on failure")

    • C#禁止使用许多名称但合法的IL

    • IL允许你define your own parameterless constructors for value types .

    • 您无法在C#中使用"raise"元素定义事件 . (在VB中,您必须自定义事件,但"default"事件不包括一个 . )

    • CLR允许某些转换,但C#不允许转换 . 如果你通过C#中的 object ,这些有时会起作用 . 有关示例,请参阅uint[]/int[] SO question .

    如果我想到别的什么,我会加上这个......

  • 3

    MSIL允许重载仅因返回类型而异

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

    要么

    callvirt int32 ...
    
  • 3

    大多数.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,它必须手动实现 . (抖动也可以自由地进行优化,但这是另一个问题 . )

  • 8

    在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
    
  • 6

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

  • 9

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

  • 2

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

  • 1

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

  • 10

    在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做到这一点

  • 18

    使用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
    
  • 7

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

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

  • 4

    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我发现了一些 .

  • 14

    你可以破解方法覆盖co / contra-variance,C#没有更多关于实现这个here的信息,以及部分12

  • 4

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

  • 28

    这里还有一些:

    • 您可以在委托中使用额外的实例方法 .

    • 代理可以实现接口 .

    • 您可以在委托和接口中拥有静态成员 .

  • 17

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

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

    测得的结果代码比C#中的等效代码快10倍(在每个字节上进行异或) .

    ===

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

  • 20

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

  • 19

    枚举继承是不可能的:

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

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

  • 34

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

    //以下类定义是非法的:public class YourCustomDelegate:MulticastDelegate {}

  • 9

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

相关问题