65.9K
CodeProject 正在变化。 阅读更多。
Home

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

2015 年 2 月 18 日

CPOL

2分钟阅读

viewsIcon

76156

理解结构体 - 为什么 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 在栈上扩展内存以支持赋值吗?

此外 - structs 作为值类型,当然不使用引用(除非装箱),这将使多态性变得毫无意义,因为没有通过引用指针的间接引用。

另外,考虑数组的情况 - 值类型数组以“内联”方式存储(为了性能和垃圾回收的原因)。如果使用一个 struct 而不知道它实际上是一个“派生的 struct”,它将导致内存损坏。

总而言之 - 我们已经看到了为什么 .NET 允许继承一个 struct 是非常不合理的。好消息是,你总是可以继承...嗯...一个类

© . All rights reserved.