在MSIL中,有哪些C#或VB.NET无法实现的功能?

172

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

在MSIL中,我们也可以更轻松地完成一些任务,而不像C#、VB.NET、F#、j#或其他任何.NET语言那样麻烦。

到目前为止,我们有以下这些:

  1. 尾递归
  2. 泛型协变/逆变(在C#4和VB 10中允许)
  3. 仅返回类型不同的重载
  4. 覆盖访问修饰符
  5. 创建一个无法从System.Object继承的类
  6. 筛选异常(在VB和C#6中允许)
  7. 调用当前静态类类型的虚拟方法。
  8. 获取值类型的装箱版本的句柄。
  9. 进行try / fault操作。
  10. 使用被禁止的名称。
  11. 为值类型定义自己的无参构造函数
  12. 定义具有raise元素的事件。
  13. CLR允许的某些转换在C#中不允许。
  14. 将非main()方法作为.entrypoint
  15. 直接使用本机int和本机unsigned int类型。
  16. 使用瞬态指针进行操作
  17. 在MethodBodyItem中使用emitbyte指令。
  18. 抛出和捕获非System.Exception类型。
  19. 继承枚举(未经验证)
  20. 您可以将字节数组视为(4倍较小的)整数数组。
  21. 一个字段/方法/属性/事件都可以拥有相同的名称(未经验证)。
  22. 从catch块返回到try块内部。
  23. 您可以使用famandassem访问修饰符(protected internal是famorassem,但在C# 7.2和VB 15.5中现在已允许)。
  • 可以直接访问<Module>类来定义全局函数或模块初始化器。
  • 创建并使用非零起始的1-based数组。
  • 创建开放实例和封闭静态委托,以及getter/setter的委托。
  • 不使用临时变量交换两个值。
  • 能够显式实现任何名称的接口,并在一个函数中实现两个接口(VB可实现)。
  • 声明vtfixup(C中等同于extern
  • 指定任意的modoptmodreq

  • 6
    F# 支持尾递归,请参阅:http://en.wikibooks.org/wiki/F_Sharp_Programming/Recursion - Bas Bossink
    5
    继承枚举?有时那真的太好了。 - Jimmy Hoffa
    1
    在.NET中,Main方法的M是大写的。 - Concrete Gannet
    7
    “将问题标记为‘无建设性’是荒谬的。这是一个实证问题。” - Jim Balter
    20个回答

    9

    +1 点!正是如此,这在 C# 中是一个很大的遗漏,就像我在这里注意到的那样。 (https://dev59.com/p0_Ta4cB1Zd3GeqPAmoS#3363783) - Abel

    8

    本机类型
    您可以直接使用本机int和本机unsigned int类型(在c#中,您只能使用不同的IntPtr进行操作。

    瞬时指针
    您可以使用瞬态指针,这些指针是指向托管类型的指针,但保证不会移动,因为它们不在托管堆中。不完全确定您如何在不与非托管代码混淆的情况下有用地使用它,但其他语言无法直接访问它,只能通过诸如stackalloc之类的东西访问。

    <Module>
    如果您愿意,可以玩弄该类(您可以通过反射而无需需要IL来做到这一点)

    .emitbyte

    15.4.1.1 .emitbyte指令 MethodBodyItem :: = ... | .emitbyte Int32 此指令导致未签名的8位值直接发出到方法的CIL流中,在指令出现的位置。 [注意:.emitbyte指令用于生成测试。在生成常规程序时不需要它。结束注释]

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

    请阅读规范,我相信您会找到更多内容。


    @supercat,对于一个瞬态指针来说这是行不通的,因为所涉及的数据可能在堆上。你想要的是Eric在这里谈到的ref returns:http://blogs.msdn.com/b/ericlippert/archive/2011/06/23/ref-returns-and-ref-locals.aspx - ShuggyCoUk
    @ShuggyCoUk:有趣。我真的希望能说服Eric提供一种方法,使“DictOfPoints(key)。X = 5;”可以正常工作。现在,如果想要硬编码DictOfPoints以仅与Point类型(或其他特定类型)一起使用,几乎可以实现,但很麻烦。顺便说一句,我想看到的一件事是一种方式,可以编写开放式的通用函数,例如DoStuff<...>(someParams,ActionByRef<moreParams,...>,...),它可以根据需要扩展。我猜在MSIL中有一些方法可以做到这一点;需要一些编译器的帮助... - supercat
    @ShuggyCoUk:...这将提供另一种通过引用属性的方式,额外的好处是,在外部对引用所做的所有操作完成后,某些属性可以运行。 - supercat
    @supercat 如果在堆栈生命周期内传递值类型的引用是可以的,但如果在外部进行这样的操作就会变得非常复杂,因为您可能会以复杂的方式显着改变生命周期。我承认您可以使用它做一些很酷的事情,但我不确定它应该成为 C# 这种语言的一部分。一个关键的问题是它极大地复杂化了类型系统(您不能将它们装箱到对象中,您不能在泛型类型中使用它们,在反射方案中有更多选项等)。我认为这并不值得为该语言和框架的其他部分增加额外的成本。 - ShuggyCoUk
    @ShuggyCoUk 让我们在聊天室里继续讨论 - supercat
    显示剩余6条评论

    6
    您可以黑掉方法覆盖的协变/逆变,而C#并不支持这一点(这与泛型变异不同!)。我在这里提供了更多关于实现的信息这里,以及第一部分1和第二部分2

    4

    以下是更多内容:

    1. 委托可以有额外的实例方法。
    2. 委托可以实现接口。
    3. 委托和接口中可以有静态成员。

    4

    我认为我一直渴望的(原因完全错误)是枚举中的继承。在SMIL中这似乎并不难做到,因为枚举只是类,但在C#语法中却不能这样做。


    4

    20) 可以将字节数组视为一个(4倍小的)整数数组。

    最近我用这个方法实现了快速XOR,因为CLR的XOR函数是基于整数操作的,而我需要在字节流上执行XOR操作。

    结果代码的性能比C#中等效的方法(对每个字节进行XOR)提高了约10倍。

    ===

    由于我的stackoverflow信誉不够,无法编辑问题并将其添加到列表中作为第20个问题。如果有人可以帮忙添加,那就太好了 ;-)


    3
    你可以使用不安全指针来完成这个任务,而不必涉及不可变性借用(immutable borrowing)问题。我想这样做可能同样快,甚至更快一些,因为它不会进行任何边界检查。 - P Daddy

    3

    混淆器使用的一种技术是,可以使字段/方法/属性/事件具有相同的名称。


    1
    我在我的网站上放了一个示例:http://jasonhaley.com/files/NameTestA.zip在那个压缩包里有IL和一个包含以下所有内容的类的exe: -类名为A -事件名为A -方法名为A -属性名为A -2个字段名为A我找不到一个好的参考资料可以指引你,虽然我可能是在ecma 335规范或Serge Lidin的书中读到的。 - Jason Haley

    2

    您也可以在IL中从System.Multicast委托派生一个类,但是在C#中无法这样做:

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

    public class YourCustomDelegate: MulticastDelegate { }


    2

    IL允许您定义模块级别(也称为全局)方法,而相比之下,C#只允许您定义附加至少一个类型的方法。


    2

    枚举继承实际上是不可能的:

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

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


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接