为什么呼吁值类型显式接口实现导致它被装箱?接口、类型

2023-09-04 22:45:18 作者:念你的温柔

我的问题是有些涉及到这一块:How确实有一个隐含实现的接口值类型的泛型约束prevent拳?,但不同的,因为它不应该需要一个约束要做到这一点,因为它不是一般的。

My question is somewhat related to this one: How does a generic constraint prevent boxing of a value type with an implicitly implemented interface?, but different because it shouldn't need a constraint to do this because it's not generic at all.

我有code

interface I { void F(); }
struct C : I { void I.F() {} }
static class P {
    static void Main()
    {    
        C x;
        ((I)x).F();
    }
}

的主要方法编译成这样的:

The main method compiles to this:

IL_0000:  ldloc.0
IL_0001:  box        C
IL_0006:  callvirt   instance void I::F()
IL_000b:  ret

为什么不把它编译成这样?

Why doesn't it compile to this?

IL_0000:  ldloca.s   V_0
IL_0002:  call       instance void C::I.F()
IL_0007:  ret

我明白为什么你需要一个方法表进行虚拟呼叫,但你并不需要在这种情况下,虚拟呼叫。如果接口实现通常它不会使虚拟呼叫。

I see why you need a method table to make a virtual call, but you don't need to make a virtual call in this case. If the interface is implemented normally it doesn't make a virtual call.

也有关系:Why是显式接口实现私有 - ?在这个问题上存在的答案并不能充分解释为什么方法被标记为私人的元数据(而不是仅仅具有不可用名)。但是,即使这并不能完全解释为什么它的盒装,因为从内C中调用时,它仍然盒。

Also related: Why are explicit interface implementations private? - the existing answers on this question don't adequately explain why the methods are marked as private in the metadata (rather than merely having unusable names). But even this doesn't fully explain why it's boxed, since it still boxes when called from inside C.

推荐答案

我想答案是在C#规范的接口如何处理。从规格:

I think the answer is in the C# specification of how interfaces can be treated. From the Spec:

有几种   在C#中,包括字段变量,   数组元素,局部变量和   参数。再present变量   存储位置,和每一个变量   有一个类型,以决定什么   值可以被存储在变量,   如由下表

There are several kinds of variables in C#, including fields, array elements, local variables, and parameters. Variables represent storage locations, and every variable has a type that determines what values can be stored in the variable, as shown by the following table.

在它后面说,对接口表

有一个空引用,引用类类型的实例   实现该接口类型,或一   参照值的装箱值   实现该接口类型   类型

A null reference, a reference to an instance of a class type that implements that interface type, or a reference to a boxed value of a value type that implements that interface type

它说明确,这将是一个值类型的装​​箱值。编译器只是服从规范

It says explicitly that it will be a boxed value of a value type. The compiler is just obeying the specification

要添加基于注释的更多信息。编译器可以自由地改写,如果它具有相同的效果,但由于拳击时,你做值的副本类型不具有相同的值类型。从规格一次:

To add more information based upon the comment. The compiler is free to rewrite if it has the same effect but because the boxing occurs you make a copy of the value type not have the same value type. From the specification again:

装箱转换意味着制作   值的副本被装箱。这是   从一个转换不同   引用类型为类型的对象,在   该值继续引用   相同的情况下,简单地是   视为少派生类型   对象。

装箱与拆箱

A boxing conversion implies making a copy of the value being boxed. This is different from a conversion of a reference-type to type object, in which the value continues to reference the same instance and simply is regarded as the less derived type object.

这意味着它有充分的时间做拳击,或者你会得到不一致的行为。这方面的一个简单的例子可以通过执行与所提供的程序如下显示:

This means it has to do the boxing every time or you'd get inconsistent behavior. A simple example of this can be shown by doing the following with the provided program:

public interface I { void F(); }
public struct C : I {
    public int i;
    public void F() { i++; } 
    public int GetI() { return i; }
}

    class P
    {
    static void Main(string[] args)
    {
        C x = new C();
        I ix = (I)x;
        ix.F();
        ix.F();
        x.F();
        ((I)x).F();
        Console.WriteLine(x.GetI());
        Console.WriteLine(((C)ix).GetI());
        Console.ReadLine();
    }
}

我添加了一个内部​​成员结构体 C 由1每一个 F()被调用时增加上该对象。这让我们看到了什么是发生在我们的价值类型的数据。如果未在进行X 拳击那么你所期望的程序写出来的4为呼叫格提()我们称之为 F()四倍。然而,我们得到的实际结果是1和2中的原因是,拳击取得的副本。

I added an internal member to struct C that is incremented by 1 every time that F() is called on that object. This lets us see what is happening to the data of our value type. If boxing was not performed on x then you would expect the program to write out 4 for both calls to GetI() as we call F() four times. However the actual result we get is 1 and 2. The reason is that the boxing has made a copy.

这表明了我们有如果我们框中的值和之间的区别,如果我们不框中的值

This shows us that there is a difference between if we box the value and if we don't box the value