所有用.NET语言编写的代码都编译成MSIL,但是你是否只能直接使用MSIL执行特定的任务/操作?
让我们在MSIL中比C#,VB.NET,F#,j#或任何其他.NET语言更容易完成任务。
到目前为止,我们有这个:
raise
元素定义事件。main()
方法作为.entrypoint
。int
和原生unsigned int
类型。protected internal
是fam 或汇总)<Module>
类以定义全局函数或模块初始值设定项。答案 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)
可以合并protected
和internal
访问修饰符。在C#中,如果编写protected internal
,则可以从程序集和派生类访问成员。通过MSIL,您可以获得一个可以从程序集中中的派生类访问的成员。 (我认为这可能非常有用!)
答案 4 :(得分:18)
看起来你已经有了很好的答案。另外:
object
,这些有时会有效。有关示例,请参阅uint[]/int[] SO question。如果我想到其他任何事情,我会加上这个...
答案 5 :(得分:17)
CLR已经支持通用协同/反演,但C#直到4.0才获得此功能
答案 6 :(得分:14)
在IL中,您可以抛出并捕获任何类型,而不仅仅是从System.Exception
派生的类型。
答案 7 :(得分:10)
对于虚方法调用,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()
。
答案 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)
答案 13 :(得分:4)
我认为我一直希望的(完全错误的原因)是Enums中的继承。在SMIL中做起来似乎并不困难(因为Enums只是类),但它不是C#语法要求你做的事情。
答案 14 :(得分:4)
还有一些:
答案 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#只允许您定义方法,只要它们附加到至少一种类型。