Ответ 1
Причина в том, что большинство методов наследования относятся к полиморфизму во время выполнения (виртуальные функции), а те не работают с типами значений: для полиморфизма времени выполнения есть какое-либо значение, объекты должны рассматриваться как ссылки - это тоже не относится к .NET, это просто техническая информация о том, как реализуются виртуальные функции.
Типы значений формируют исключение из правила .NET, точно для того, чтобы облегченные объекты не требовали косвенности через ссылки. Поэтому полиморфизм во время выполнения не работает для них, и большинство аспектов наследования становятся бессмысленными.
(Theres исключение: объект типа значения может быть помещен в бокс, что позволяет вызывать виртуальные методы, унаследованные от System.Object
.)
Чтобы обратиться к одному из ваших пунктов:
- Вы можете отличить от производной структуры до базы, поскольку они будут перекрывать одну и ту же память.
Нет, это было бы невозможно - выбор типа значения скопировал бы его значение. Здесь не были ссылки на ссылки, поэтому в памяти не было перекрытия. Поэтому отличать тип значения к его базовому типу бессмысленно (опять же, если не говорить о преобразовании в object
, который фактически выполняет бокс под капотом, а также работает с копией значения).
Все еще не ясно? Давайте посмотрим на пример.
Допустим, что мы получили гипотетический struct Shape
и, наследуя от него, struct Circle
. Shape
определяет виртуальный Draw
метод (который принимает объект Graphics
). Теперь, скажем, мы хотим нарисовать фигуру на холсте. Это, конечно, отлично работает:
var circle = new Circle(new Point(10, 10), 20);
circle.Draw(e.Graphics); // e.Graphics = graphics object of our form.
- Но здесь мы вообще не используем наследование. Чтобы использовать наследование, представьте вместо этого следующий вспомогательный метод DrawObject
:
void DrawObject(Shape shape, Graphics g) {
// Do some preparation on g.
shape.Draw(g);
}
И мы называем это в другом месте с помощью Circle
:
var circle = new Circle(new Point(10, 10), 20);
DrawObject(circle, e.Graphics);
- И, ka-blam - этот код не рисует круг. Зачем? Потому что, когда мы передаем круг методу DrawObject
, мы делаем две вещи:
- Скопируем его.
- Мы срезаем его, т.е. объект
Shape
действительно больше не являетсяCircle
- ни оригинальным, ни копией. Вместо этого его частьCircle
была "нарезана" во время копирования, и остается только частьShape
.shape.Draw
теперь вызывает методDraw
Shape
, а неCircle
.
В С++ вы можете фактически вызвать это поведение. По этой причине ООП на С++ работает только по указателям и ссылкам, а не по типам значений напрямую. И по той же причине .NET разрешает наследование ссылочных типов, потому что в любом случае вы не можете использовать его для типов значений.
Обратите внимание, что приведенный выше код работает в .NET, если Shape
является интерфейсом. Другими словами, ссылочный тип. Теперь ситуация другая: ваш объект Circle
все равно будет скопирован, но он также будет помещен в ссылку.
Теперь .NET теоретически позволит вам наследовать struct
от class
. Тогда приведенный выше код будет работать так же хорошо, как если бы Shape
был интерфейсом. Но тогда все преимущество использования struct
в первую очередь исчезает: для всех целей и целей (кроме локальных переменных, которые никогда не передаются другому методу, следовательно, нет полезности наследования), ваш struct
будет вести себя как неизменяемый ссылочный тип вместо типа значения.