当delegate关键字在C#所使用的,C#编译器自动生成从派生的类System.MulticastDelegate的类.
这个编译器生成的类也包含3个方法:Invoke, BeginInvoke and EndInvoke.
所有这三种方法都有标记,public virtual extern但有趣的是,类本身已被标记sealed.
在密封类中定义的虚拟方法不仅违反直觉,而且在C#中实际上是非法的.
所以我的问题是,是否有一个特定的原因,或者它只是其中一个无害的事情,记住一些假设的未来增强?
编辑1:
可能的原因是强制使用'callVirt'IL操作码而不是'call',以便在尝试执行这三种方法中的任何一种之前,CLR始终检查委托对象为空?虽然我不明白为什么delegate在这方面应该是一个特例.
也不是强制使用的性能打击callvirt(虽然它可能是微不足道的)
编辑2:
添加了CIL标记,因为事实证明定义委托的C#方式实际上是由CIL标准强制执行的.标准规定(以下不是全文)
代理应具有System.Delegate的基本类型.代表应宣布密封,代表的唯一成员应为此处规定的前两种或全部四种方法.这些方法应声明为运行时和管理.它们不应具有身体,因为该身体应由VES自动创建.委托上可用的其他方法继承自基类库中的System.Delegate类.委托方法是:
- 实例构造函数
- Invoke方法应该是虚拟的
- BeginInvoke方法(如果存在)应为虚拟方法
- EndInvoke方法应该是虚拟的
所以这绝对不是编译器进程的副作用,或者类似于其他有趣的编译器输出.
如果标准强调某些东西,那必须是出于某种充分的理由和理由.
所以现在的问题是为什么代表们的CIL标准同时强调密封和虚拟?
渔获物在这里吗?:
它们不应具有身体,因为该身体应由VES自动创建.
它们是否标记为虚拟,以便在调用这些方法时可以执行VES/CLR生成的主体?
有没有办法在C#中执行一系列IL代码,比如C/C++中的shell代码?
我想创建一个方法,将其转换为IL代码,对其进行模糊处理并存储在一个字节数组中,最后要执行它来解密数组内容并执行IL代码.
例如,这是我的C#代码:
static int MyMethod(string A, int B)
{
B += 10;
if (A.Equals("A"))
B = 0;
return B;
}
Run Code Online (Sandbox Code Playgroud)
现在我将它转换为IL代码:
private static int MyMethod(string A, int B)
{
locals: int local_0,
bool local_1
/* 0000025C 00 */ nop
/* 0000025D 03 */ ldarg_1 // int B
/* 0000025E 1F 0A */ ldc_i4_s 10
/* 00000260 58 */ add
/* 00000261 10 01 */ starg_s arg_1 // int B
/* 00000263 02 */ ldarg_0 // string A
/* 00000264 …Run Code Online (Sandbox Code Playgroud) 出于教育目的,我正在学习一些IL(主要是因为我很好奇发生了什么'%'在幕后(原来是rem)并开始离题......).
我写了一个方法,只是返回true来解决一些问题并且想知道'br.s'操作码:
.method public hidebysig static bool ReturnTrue() cil managed
{
// Code size 7 (0x7)
.maxstack 1
.locals init ([0] bool CS$1$0000)
IL_0000: nop
IL_0001: ldc.i4.1
IL_0002: stloc.0
IL_0003: br.s IL_0005
IL_0005: ldloc.0
IL_0006: ret
} // End of method Primes::ReturnTrue
Run Code Online (Sandbox Code Playgroud)
在ldc.i4.1在堆栈上推送1并且stloc.0将其放在第0个本地之后,br.s基本上(据我所知)在行IL_0005处对ldloc.0执行'goto'.
为什么是这样?为什么没有IL_0004线,所以这可以省略?
我在C#中有以下代码
// test.Program
private static void Main()
{
int x = 5;
int y = 100;
Console.WriteLine(y + ", " + x);
}
Run Code Online (Sandbox Code Playgroud)
而且我正在阅读IL代码,我之前从未编写过程序集,所以我问我每行的功能是否正确.
.method private hidebysig static
void Main () cil managed
{
// Method begins at RVA 0x2058
// Code size 33 (0x21)
.maxstack 3 // maximum stack in this method is 3
.entrypoint // method is initial entry point
.locals init ( // reserves memory for x and y variables
[0] int32 x, // x variable is …Run Code Online (Sandbox Code Playgroud) 我如何获得C#代码的IL代码?我可以使用extern库或内部函数吗?
编辑:我想用我的应用程序在MessageBox中显示IL代码.
有了这个简单的C#代码,我就跑了csc hello.cs; ildasm /out=hello.txt hello.exe.
class Hello
{
public static void Main()
{
System.Console.WriteLine("hi");
}
}
Run Code Online (Sandbox Code Playgroud)
这是来自ildasm的IL代码.
.class private auto ansi beforefieldinit Hello
extends [mscorlib]System.Object
{
.method public hidebysig static void Main() cil managed
{
.entrypoint
// Code size 13 (0xd)
.maxstack 8
IL_0000: nop
IL_0001: ldstr "hi"
IL_0006: call void [mscorlib]System.Console::WriteLine(string)
IL_000b: nop
IL_000c: ret
} // end of method Hello::Main
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7) …Run Code Online (Sandbox Code Playgroud) 这里有一些c#的测试程序:
using System;
struct Foo {
int x;
public Foo(int x) {
this.x = x;
}
public override string ToString() {
return x.ToString();
}
}
class Program {
static void PrintFoo(ref Foo foo) {
Console.WriteLine(foo);
}
static void Main(string[] args) {
Foo foo1 = new Foo(10);
Foo foo2 = new Foo(20);
Console.WriteLine(foo1);
PrintFoo(ref foo2);
}
}
Run Code Online (Sandbox Code Playgroud)
这里反汇编编译方法主要:
.method private hidebysig static void Main (string[] args) cil managed {
// Method begins at RVA 0x2078
// Code size 42 (0x2a)
.maxstack …Run Code Online (Sandbox Code Playgroud) 当Joel Spolsky和Jeff Atwood在他们的播客中就程序员是否应该学习C而开始存在分歧时,无论他们的行业和交付平台如何,它在开发者社区中引发了一场爆炸性的辩论,这可能仍然在今天的某些群体中肆虐.我一直在阅读一些程序员博主的一些段落,他们对此事采取了行动.来自双方的争论当然具有重要性,我所没有发现的是一个与开发人员专注于.NET Framework的观点截然不同的视角.几乎所有人都在评论一般的程序员观点.
我想要的是什么?回想一下杰夫阿特伍德的观点,即大多数时候开发者处于如此高水平的花费将是学习业务/领域,除了学习技术以实现这些领域要求所需的一切.在我的工作经历中,这是对许多人工作生活的非常准确的描述.现在假设.NET开发人员可以派生的"课外"学习的时候,应该是为C?
为了记录,我自己在学校里学习过C,我完全理解和欣赏支持者的推理.但是,在考虑事情时,我个人认为.NET开发人员不应该直接进入C.因为,我希望更多的开发人员需要花些时间学习的东西是 - MSIL和CLR.
也许我被一群不同寻常的同事困住了,我不知道,但在我看来很多人并没有意识到他们的C#或VB代码在JIT进入之前首先在IL编译并使其成为原始机器码.大多数人不知道的IL,并且在如何不感兴趣准确的CLR处理他们编写的代码.通过C#阅读Jeffrey Richter的CLR对我来说在很多方面都让我感到震惊; 很高兴我读到它,尽管同事们认为它"太低了".我不是IL的专家,但是在基础知识方面,我发现自己更容易跟随他的文本,因为我已经熟悉了IL的堆栈行为.我发现自己拆解程序集以查看在编写某些代码时IL的结果.
我学习了CLR和MSIL,因为我知道这是我下面的直接层.允许我执行自己的工作层的图层.C,实际上是进一步下降.更接近我们的"现实"的是CLR和MSIL.这就是为什么我会建议其他人去那些,因为我没有看到足够的人在钻研那层.或者,您的团队是否已全部熟悉MSIL?
是否有一个属性可以告诉编译器必须始终优化方法,即使/o+未设置全局编译器开关?
我问的原因是因为我想要动态创建一个基于现有方法的IL代码的方法; 当代码被优化时,我想要做的操作相当容易,但由于编译器生成了额外的指令,因此在非优化代码中变得非常困难.
编辑:关于非优化的更多细节困扰我...
让我们考虑以下阶乘函数的实现:
static long FactorialRec(int n, long acc)
{
if (n == 0)
return acc;
return FactorialRec(n - 1, acc * n);
}
Run Code Online (Sandbox Code Playgroud)
(注意:我知道有更好的方法来计算阶乘,这只是一个例子)
在启用优化的情况下生成的IL非常简单:
IL_0000: ldarg.0
IL_0001: brtrue.s IL_0005
IL_0003: ldarg.1
IL_0004: ret
IL_0005: ldarg.0
IL_0006: ldc.i4.1
IL_0007: sub
IL_0008: ldarg.1
IL_0009: ldarg.0
IL_000A: conv.i8
IL_000B: mul
IL_000C: call UserQuery.FactorialRec
IL_0011: ret
Run Code Online (Sandbox Code Playgroud)
但是未经优化的版本是完全不同的
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldc.i4.0
IL_0003: ceq
IL_0005: ldc.i4.0
IL_0006: ceq
IL_0008: stloc.1
IL_0009: ldloc.1
IL_000A: brtrue.s IL_0010
IL_000C: ldarg.1 …Run Code Online (Sandbox Code Playgroud) 当我主要使用c ++时,虽然了解汇编并写了一些非试验性的asm代码是至关重要的,这样我才能真正理解正在发生的事情.我现在主要做.net,虽然我对IL有所了解,但我并不精通.
IL是一个应该具备知识的技能,还是将更全面地学习IL继续告知我如何使用和编写.net代码?