5

我相信这是一个非常简单的问题。谁能解释为什么这段代码输出 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;
        }
    }
}
4

3 回答 3

8

将值类型 ( struct) 转换为接口将值框起来。因此,您是在值的盒装副本上调用该方法,而不是在值本身上。

于 2013-02-14T15:53:23.357 回答
1

值类型 ( struct) 通过传递,但接口被认为是引用类型(而不是值类型)。让我们来看看:

Bus b = new Bus(1000);

现在b包含 a 的Bus其音量设置为 1000。

Car c = (Car)b;

现在将 in 中的值b复制制成 的引用类型(装箱Car。现在c包含一个指向盒装副本的指针。

c.IncreaseVolume(50);

在引用类型上,您调用IncreaseVolume,它是Car接口的成员。它接收对装箱值的引用。它需要一个指向框中值的托管指针(使其再次成为值类型)。

void Car.IncreaseVolume(int amount)
{
    ((Bus)this).IncreaseVolume(amount);
}

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

public void IncreaseVolume(int amount)
{
    volume += amount;
}

现在该方法返回。请注意,没有任何操作对 中的值起作用b,仅对它的副本起作用。所以下一条语句将打印1000

Console.WriteLine(b.GetVolume());

就是这样。

于 2013-02-14T16:03:14.867 回答
0

尽管有时值类型实现可变接口很有用,但必须非常小心地使用这些类型。C# 假装所有值类型都是对象的事实在这方面有点无益(实际上,它们是可转换为的东西Object),因为这意味着没有办法让编译器警告不正确的使用。

除了完全忽略接口的选项之外,通常需要使用变异接口类型的代码必须做以下两件事之一:

  1. 将结构的实例转换为接口类型,并且永远不要再将其转换为接口类型、“Object”或“ValueType”以外的任何内容。特别是,永远不要将它转换回它自己的类型。请注意,不同接口或引用类型之间的转换很好,只要实例永远不会转换回它自己的类型。这是 `List.Enumerator` 之类的最常见的模式,它经常被强制转换为 `IEnumerator` 而从不强制转换为其他任何东西。
  2. 切勿将结构用作接口类型,除非将其作为受约束的通用 `ref` 参数传递给任何应该修改它的方法。执行此操作的代码通常在语法上“笨拙”,但在语义上是正确的。结构分配可用于获取结构形状的快照这一事实可能允许通过其他方式不容易或有效地获得语义,但是有许多方法可以犯错误,不会导致编译器诊断但不会产生正确的行为。

在大多数情况下,如果需要一些东西来实现变异接口,那么所讨论的类型应该是一个类。可变结构通常只能通过直接修改底层字段或使用对传递的实例进行操作的静态方法来允许突变ref

于 2013-02-18T23:08:52.840 回答