为什么结构中的这个值根本没有改变
本文关键字:改变 结构 为什么 | 更新日期: 2023-09-27 17:59:03
我相信这是一个非常简单的问题。有人能解释为什么这个代码输出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
包含卷设置为1000的Bus
的值。
Car c = (Car)b;
现在,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());
就是这样。
尽管值类型实现可变接口有时很有用,但使用此类类型时必须格外小心。C#假装所有值类型都是对象,这在这方面有点无益(事实上,它们是可转换为Object
的东西),因为这意味着没有办法让编译器警告错误的使用。
除了完全忽略接口的选项外,通常有必要使用可变接口类型的代码必须做以下两件事之一:
- 将结构的实例强制转换为接口类型,再也不要将其强制转换为除接口类型"Object"或"ValueType"之外的任何类型。特别是,永远不要把它重新塑造成自己的类型。请注意,在不同的接口或引用类型之间进行强制转换是可以的,前提是实例永远不会被强制转换回自己的类型。这是像"列表"这样的东西最常见的模式。Enumerator",它通常被强制转换为"IEnumerator",而从不强制转换为其他任何值。
- 永远不要将结构用作接口类型,除非将其作为受约束的泛型"ref"参数传递给任何应该修改它的方法。这样做的代码通常在语法上"笨拙",但在语义上是正确的。结构赋值可以用来获取结构形状的快照,这一事实可能允许通过其他方式不容易或有效地获得语义,但有很多方法可以出错,这些错误不会导致编译器诊断,但不会产生正确的行为。
在大多数情况下,如果需要一些东西来实现一个可变的接口,那么有问题的类型应该是一个类。可突变结构通常只应通过直接修改底层字段或使用对ref
传递的实例进行操作的静态方法来允许突变。