C# 为什么结构中的这个值根本没有改变?

C# 为什么结构中的这个值根本没有改变?,c#,interface,C#,Interface,我相信这是一个非常简单的问题。有人能解释为什么这个代码输出1000而不是1050吗 public class Program { public static void Main() { Bus b = new Bus(1000); ((Car)b).IncreaseVolume(50); Console.WriteLine(b.GetVolume()); } }

我相信这是一个非常简单的问题。有人能解释为什么这个代码输出1000而不是1050吗

public class Program
    {
        public static void Main()
        {
            Bus b = new Bus(1000);
            ((Car)b).IncreaseVolume(50);
            Console.WriteLine(b.GetVolume());
        }
    }

    public interface Car
    {
        int GetVolume();
        void IncreaseVolume(int amount);
    }

    public struct Bus : Car
    {
        private int volume;

        public Bus(int volume)
        {
            this.volume = volume;
        }

        public int GetVolume()
        {
            return volume;
        }

        public void IncreaseVolume(int amount)
        {
            volume += amount;
        }
    }
}
将值类型(
struct
)强制转换为接口会将该值框起来。因此,您是在值的盒装副本上调用方法,而不是在值本身上调用方法。

值类型(
struct
)是按值传递的,但接口被视为引用类型(而不是值类型)。让我们看看:

Bus b = new Bus(1000);
现在
b
包含一个
Bus
的值,其音量设置为1000

Car c = (Car)b;
现在,
b
中的值被复制并制成
Car
的参考类型(已装箱)。现在
c
包含一个指向装箱副本的指针

c.IncreaseVolume(50);
在参考类型上,您调用
IncreaseVolume
,它是
Car
界面的成员。它接收对装箱值的引用。它接受指向框中值的托管指针(使其再次成为值类型)

现在,您的方法将作用于框中的值:

public void IncreaseVolume(int amount)
{
    volume += amount;
}
现在该方法返回。请注意,在
b
中,没有任何操作对值起作用,只对其副本起作用。因此,下一条语句将打印
1000

Console.WriteLine(b.GetVolume());

就是这样。

尽管有时值类型实现变异接口很有用,但使用此类类型时必须格外小心。事实上,C#假装所有值类型都是对象在这方面是没有帮助的(实际上,它们是可转换为
对象
),因为这意味着编译器无法警告错误的使用

除了完全忽略接口的选项外,通常有必要使用变异接口类型的代码必须执行以下两项操作之一:

  • 将结构实例强制转换为接口类型,并且不再将其强制转换为接口类型、“对象”或“值类型”以外的任何类型。特别是,永远不要将其转换回自己的类型。请注意,在不同接口或引用类型之间进行强制转换是可以的,前提是实例永远不会被强制转换回自己的类型。这是类似“List.Enumerator”的最常见模式,它通常转换为“IEnumerator”,而从不转换为任何其他模式。
  • 永远不要将结构用作接口类型,除非将其作为受约束的泛型'ref'参数传递给任何应该修改它的方法。执行此操作的代码通常在语法上“笨拙”,但在语义上是正确的。结构赋值可用于获取结构形状的快照,这一事实可能允许通过其他方式不容易或有效地获得语义,但有许多方法会导致错误,这些错误不会导致编译器诊断,但不会产生正确的行为。
    在大多数情况下,如果需要一些东西来实现一个变异接口,那么所讨论的类型应该是一个类。可变结构通常只允许通过直接修改基础字段或使用对
    ref

    传递的实例进行操作的静态方法进行变异,因为
    Bus
    结构。把它变成一个
    。正如你的问题标题所暗示的那样,你不是在把
    转换成一个接口,而是一个
    结构
    。谢谢你,我以为我是在处理类。愚蠢的错误,可能会被关闭。甚至不需要强制转换,因为
    Bus
    (作为类,而不是结构)必须实现该方法,所以调用
    ((Car)b)。IncreaseVolume(50)
    b.IncreaseVolume(50)
    完全相同,提供的总线是一个类。@JohnWillemse:我想OP知道这一点,这是一个简化的例子。考虑一个方法,它使用
    汽车
    并在内部调用
    递增音量
    ,然后在使用
    总线
    调用该方法后,您调用
    GetVolume
    ,值没有改变,您想知道为什么。使用
    对象可以很容易地显示这一点。ReferenceEquals
    object.ReferenceEquals(b,(Car)b)
    将为
    false
    @DanielHilgarth:这表明,当您将一个值装箱两次时,它每次都会创建一个新对象。实际上没有办法检查值和引用是否相同。根据定义,它们已经不是了。“框两次”,因为
    ReferenceEquals
    的参数属于
    object
    类型,并且传递值类型将已经将其框起来?@DanielHilgarth:是的,没错。我很确定对包含结构的变量调用方法之间没有区别,包含结构的对象或包含结构的数组元素。无需先复制该值。您是指额外变量
    Car c
    ?这只是为了解释。我的意思是“它通过复制装箱对象中的值来解除对该值的装箱(使其再次成为值类型)。”我认为不会发生这种情况。它怎么可能不会发生呢?毕竟,
    IncreaseVolume
    是一个值类型的实例方法,因此它的
    this
    应该是一个值类型。传递给接口方法的
    this
    是引用类型,因此需要取消绑定。值在框内。该方法可以在此基础上操作,不需要首先提取值。与数组元素中的结构值进行比较<代码>var a=新车[1];a[0]=新车(1000辆);a[0]。增加体积(50);var result=a[0]。GetVolume();//结果==1050
    Console.WriteLine(b.GetVolume());