如果C#开放了值类型的继承,会有什么问题发生?

软件工程师、主攻高级编程语言虚拟机的设计与实现

69 👍 / 17 💬

问题描述

C#里的值类型,都是不可互相继承的,如果开放了这个继承,会发生什么问题?

C#的设计让值类型不能多态,只有引用类型可以多态。

不能多态的话要继承来也没啥用。

如果要添加新的方法的话,C#提供了扩展方法,可以使用在struct上。虽说目前C#所允许的在struct上的扩展方法只能接受按值传入的“this”参数,而不像一般struct方法接受按引用传入的“this”参数。

有人提出了改进提案希望C#也支持this ref参数的扩展方法——毕竟VB.NET支持这个。请参考

Allow by-ref Extension methods. · Issue #165 · dotnet/roslyn · GitHub

如果要“继承”相同的字段布局(field layout)的话,那只要嵌入就好了。例如说:

struct CommonHeader {
  int _tag;
  int _id;
}

struct Foo {
  CommonHeader header; // { int _tag; int _id; }
  int _payload;
}

这也不需要继承。事实上为了这种目的使用继承是最错误的用法之一。

可以想像有人会想在这里使用继承,是为了能在Foo里访问tag和id字段时,不需要写header.tag / header.id,而可以直接写tag / id。这完全可以做成一个语法糖,就像Go一样,而不需要继承。

=======================================

而这么设计当然是有原因的。可惜目前

在GitHub上公开的C# Design Notes

并没有关于那么老的设计的部分,咱就只能猜了。

值类型最重要的特征是:

就这样。千万注意这跟什么栈啊堆啊啥乱七八糟的根本没关系。

那我们要是允许它多态的话会怎样呢?拿C++来举个最简单的例子

struct CommonHeader {
  int tag_;
  int id_;
};

struct Foo : public CommonHeader {
  int payload_;
};

看起来很正常?那让我们来写个函数看看:

const int MARK = 1;

void mark(CommonHeader obj) { // pass-by-value of a value type
  obj.tag_ = MARK;
}

int main() {
  Foo foo;
  mark(foo);

  return 0;
}

这里在mark()函数里发生了两件“有趣”的事:

所以我们想在C#里吃对象切糕不?多半不想吧…

=======================================

最后…

Eric Lippert大大发过很多篇关于C#的值类型的博文:

Value Types | Fabulous Adventures In Coding

。好好读,再说值类型是为了把值放在栈上的请自己拖出去打pp >_<