为委托类型生成的CIL代码是编译时代码还是运行时代码

本文关键字:代码 编译 时代 运行时 CIL 类型 | 更新日期: 2023-09-27 18:14:48

假设有一个委托引用两个方法Add()和Sub()。我所要问的是c#编译器是否在运行时或编译时生成等效的IL代码?

,

public int delegate Dele(int,int);
//methods
int Add(int a,int b)
{
    //...
}
int Sub(int a,int b)
{
    //...
}

//here comes the condition
if(cond)
{
   Del+=Add;
}
else
{
   Del+=Sub;
}
int ans=Del(4,4);

这里编译器是否在编译时或运行时为true和false条件生成cil代码?

为委托类型生成的CIL代码是编译时代码还是运行时代码

引用哪个方法仅在调用时才会解析,即在运行时。在我们的例子中,是的,它对优化有一点贡献,因为它只被调用了一次,并且在执行路径中总是只有一个CIL代码。希望这是你正在寻找的答案。

一开始就没有代表的IL。

Del+=Add;

的语法糖吗?
Del += new Dele(Add);

Dele为委托类型。在引子下,这是一个具有Invoke(int, int)方法(以及BeginInvoke/EndInvoke对)的类。

当你打电话时:

int ans=Del(4,4);

这是语法糖

int ans = Del.Invoke(4, 4);

Invoke方法没有IL代码-它被声明为virtual extern并由运行时处理。当然,调用所需的实际机器码是由JIT生成的。


下面这段话摘自第172页的CLI规范:

delegate应该被声明为sealed,并且一个delegate只能拥有前面两个方法或者所有四个方法。这些方法应声明为runtimemanaged(§II.15.4.3)。它们不应该有主体,因为主体将由VES自动创建。委托上可用的其他方法继承自基类库中的System.Delegate类(参见分区IV)。

表示编译器是否为true和编译时或运行时的错误条件?

生成两个执行路径CIL。您可以通过编译代码示例看到这一点。:

public void X(int x)
{
    Dele del;
    if(Condition(x))
    {
        del = new Dele(Add);
        del(1,1);
    }
    else
    {
        del = new Dele(Sub);
        del(2,1);
    }
}
public bool Condition(int i)
{
    return i % 2 == 0;
}

产生以下IL:

.method public hidebysig 
    instance void X () cil managed 
{
    // Method begins at RVA 0x205c
    // Code size 51 (0x33)
    .maxstack 8
    IL_0000: ldarg.0
    IL_0001: ldc.i4.5
    IL_0002: call instance bool C::Condition(int32)
    IL_0007: brfalse.s IL_001e
    IL_0009: ldarg.0
    IL_000a: ldftn instance int32 C::Add(int32, int32)
    IL_0010: newobj instance void C/Dele::.ctor(object, native int)
    IL_0015: ldc.i4.1
    IL_0016: ldc.i4.1
    IL_0017: callvirt instance int32 C/Dele::Invoke(int32, int32)
    IL_001c: pop
    IL_001d: ret
    IL_001e: ldarg.0
    IL_001f: ldftn instance int32 C::Sub(int32, int32)
    IL_0025: newobj instance void C/Dele::.ctor(object, native int)
    IL_002a: ldc.i4.2
    IL_002b: ldc.i4.1
    IL_002c: callvirt instance int32 C/Dele::Invoke(int32, int32)
    IL_0031: pop
    IL_0032: ret
} // end of method C::X

第一个代码执行可以看到从加载AddIL_000a开始。第二个可以在IL_001f中看到,其中Sub被加载。

在运行时发生的唯一一件事是创建从MulticastDelegate继承的委托,其中创建了三个方法:Invoke, BeingInvokeEndInvoke,如ECMA-335所述:

委托是通过定义派生自基类的类来创建的类型系统。代表(见分区四)。每种代表类型应提供一个名为Invoke的方法,并提供适当的参数委托的实例将对其Invoke方法的调用转发给一个或特定对象上的更多静态或实例方法delegate-assignable-to(§II.14.6.1)代表的签名。的对象和方法是在委托时选择的实例已创建。除了实例构造函数和方法,委托可以有两个额外的方法:BeginInvokeEndInvoke。这些用于异步调用。

// Nested Types
.class nested public auto ansi sealed Dele
    extends [mscorlib]System.MulticastDelegate
{
    // Methods
    .method public hidebysig specialname rtspecialname 
        instance void .ctor (
            object 'object',
            native int 'method'
        ) runtime managed 
    {
    } // end of method Dele::.ctor
    .method public hidebysig newslot virtual 
        instance int32 Invoke (
            int32 x,
            int32 y
        ) runtime managed 
    {
    } // end of method Dele::Invoke
    .method public hidebysig newslot virtual 
        instance class [mscorlib]System.IAsyncResult BeginInvoke (
            int32 x,
            int32 y,
            class [mscorlib]System.AsyncCallback callback,
            object 'object'
        ) runtime managed 
    {
    } // end of method Dele::BeginInvoke
    .method public hidebysig newslot virtual 
        instance int32 EndInvoke (
            class [mscorlib]System.IAsyncResult result
        ) runtime managed 
    {
    } // end of method Dele::EndInvoke
} // end of class Dele