在 C# 中继承结构体? 为什么(不能)?






4.51/5 (22投票s)
理解结构体 - 为什么 C# 不允许结构体被继承
引言 - 为什么还要使用结构体?
为什么使用 struct
呢?
我们都很喜欢 C# 的 struct
(这个概念对于 Java 来说比较陌生,除了原始类型之外)。当它们不需要频繁装箱时,struct
提供了一种处理相对较小且生命周期较短数据的好方法。
值类型的分配和释放通常比引用类型的分配和释放更便宜,因为 struct
要么分配在栈上,要么内联在包含类型中,并在栈回溯或包含类型释放时释放,而引用类型分配在堆上并由垃圾回收器回收。
嗯,它不允许我继承....
面向对象编程赋予我们很多能力,而面向对象的基本概念当然是继承。
我们中的许多人曾经尝试过继承一个 struct
,却发现 C# 不允许这样做。
例如,考虑以下代码
// What on earth can be wrong with this simple code?
// NOTE: DateTime is a struct, if you didn't already know it, Now() is a good time to find out :)
struct MyDateTime : DateTime
{
double SecondsFromMyBirthday { get; private set; }
}
// or with this fairly reasonable inherit request:
struct Point
{
int x, y;
}
struct Point3D : Point
{
int z;
}
一位诚实的程序员希望扩展 DateTime
结构体,为什么不呢?
这段代码会产生编译时错误:接口列表中的类型 'DateTime' 不是接口 (出现这个特定错误信息的原因是,由于不允许继承,编译器在冒号符号后只期望一个接口 - 它甚至不会考虑继承的滥用...)
如果将 struct
替换为 class,编译时错误会产生: 'MyDateTime
': 无法从密封类型 'System.DateTime
' 派生
我们理解了,System.DateTime
是(隐式地)sealed
。但是为什么?
注意:实际上,事实是所有结构体类型都隐式继承自类 System.ValueType
,而 System.ValueType
又继承自类 object.
注意:虽然结构体不能被继承,但它可以实现接口。
答案
一个 struct
已经在栈上分配了固定大小的空间。
现在考虑上面的 Point
/ Point3D
示例,看起来像一个不错的简单案例。考虑以下代码行
// Assume we have a p2d which is a Point,
// and a p3d which is a Point3D
p2d = p3d;
那么在赋值行中应该发生什么? p2d
在栈上扩展内存以支持赋值吗?
此外 - struct
s 作为值类型,当然不使用引用(除非装箱),这将使多态性变得毫无意义,因为没有通过引用指针的间接引用。
另外,考虑数组的情况 - 值类型数组以“内联”方式存储(为了性能和垃圾回收的原因)。如果使用一个 struct
而不知道它实际上是一个“派生的 struct
”,它将导致内存损坏。
总而言之 - 我们已经看到了为什么 .NET 允许继承一个 struct
是非常不合理的。好消息是,你总是可以继承...嗯...一个类!