Интерфейсы выводятся из System.Object? С# spec говорит "да", Эрик говорит "нет", реальность говорит "нет"
Вопрос прост и задан в названии.
С# 4.0 Спецификация говорит: (§4.2.2)
Тип класса объекта - это конечная базовый класс всех других типов. каждый введите С# прямо или косвенно происходит от типа класса объекта.
Эрик Липперт говорит:
Типы интерфейсов, не являющиеся классами, не выводятся из объекта.
Реальность говорит:
Type t = typeof(ICloneable).BaseType;
Console.WriteLine(t == null);
True
Значит, неправильно или что? Кому верить?
Ответы
Ответ 1
Это не такой простой вопрос, как вы думаете:)
Интерфейсы не выводятся из object
, но вы можете называть их членами object
. Таким образом, вы можете вызвать ToString()
в выражении, которое, например, имеет тип времени компиляции IDisposable
.
Кстати, я намереваюсь поговорить между Нилом Гафтером и Эриком в НДЦ, обсуждая именно этот момент...
Я считаю, что раздел 4.2.2 спецификации более упрощен, к сожалению. Надеюсь, Мэдс и Эрик исправит это для будущего выпуска - я отправлю их по почте, чтобы убедиться, что они видят этот вопрос.
Я также пытаюсь найти что-либо в спецификации, чтобы сохранить остальную часть этого ответа. Раздел 3.4.5 спецификации С# 4 подходит как можно ближе:
Элементы интерфейса - это члены, объявленные в интерфейсе и во всех базовых интерфейсах интерфейса. Члены класса object
не являются, строго говоря, членами любого интерфейса (13.2). Тем не менее, члены класса object
доступны через поиск элемента в любом типе интерфейса (7.4).
Преобразование из типа интерфейса в object
описано в разделе 6.1.6:
Неявные ссылочные преобразования:
- От любого ссылочного типа до
object
и dynamic
.
Ответ 2
Джон (как обычно) пятно на. Это не так просто, как вы думаете!
Спектр является неопределенным и слегка противоречивым. В этом конкретном случае лучше всего немного прищуриться и получить представление о том, что спецификация означает передать, а не узко разбирать его для точных определений.
Простой факт заключается в том, что "наследование" является очень чрезмерным термином в объектно-ориентированном программировании. (Кажется, я помню, что С++ имеет шесть разных видов наследования, хотя мне было бы сложно назвать их всех в кратчайшие сроки.)
Если бы у меня были мои druthers, тогда спецификация С# явно выписала бы разницу между наследованием и реализацией интерфейса. Наследование - это * метод совместного использования кода для классов (и делегатов) и типов struct (и enum), его механизм состоит в том, что все наследуемые члены базового типа становятся членами производного типа. Это в отличие от реализации интерфейса, которая требование того, что у типа реализации есть определенный набор публичных членов. Эти две вещи кажутся концептуально очень разными для меня: одна заключается в обмене существующими членами, а другая - с требованием определенных членов.
Однако спецификация не делает этого; он объединяет два под рубрикой наследования. Учитывая, что эти две несколько разные вещи имеют одно и то же имя в спецификации, трудно четко и точно рассуждать о различиях между ними.
Я лично предпочитаю думать, что объект не является "базовым типом" любого интерфейса и что члены объекта не наследуются интерфейсом. То, что вы можете называть их на экземпляре интерфейса, больше похоже на предоставленную вам компилятором любезность, так что вам не нужно вставлять приведение к объекту.
Ответ 3
Типы интерфейсов не наследуются от Object
, но места хранения типов интерфейсов содержат ссылки на объекты класса, которые (если не null) гарантированно наследуются от System.Object
.
Я думаю, что понимание того, что происходит, будет проще всего, если начать изучать разницу между типами значений и типами классов. Предположим, что у меня есть структура:
public struct SimplePoint {public int x,y;}
и у меня есть два метода
public doSomethingWithPoint(SimplePoint pt) ...
public doSomethingWithObject(Object it) ...
и кал каждый метод:
SimplePoint myPoint = ...;
doSomethingWithPoint(myPoint);
dosomethingWithObject(myPoint);
Первый вызов не передает вещь, которая происходит от Object
. Вместо этого он передает содержимое всех SimplePoint
общедоступных и частных полей. Второй вызов требует вещи, которая происходит от Object
, поэтому она генерирует новый экземпляр объекта кучи типа SimplePoint
, который содержит все публичные и частные поля типа значения SimplePoint
и загружает все эти поля с помощью соответствующие значения из myPoint
, и передает ссылку на этот объект.
Обратите внимание, что тип SimplePoint
фактически описывает два разных типа вещей: набор полей (т.е. тип значения) и тип объекта кучи. Какое значение применимо, зависит от контекста, в котором используется тип.
Типы интерфейсов имеют аналогичную морщину: при использовании в качестве типов местоположения хранилища они указывают, что место хранения будет содержать ссылку на объект. При использовании в качестве общего ограничения они ничего не говорят о том, как будет сохранен тип. Таким образом, место хранения типа интерфейса будет содержать ссылку на объект кучи, который действительно наследует от System.Object
, но переменная типа, ограниченная интерфейсом, может содержать ссылку или кучу полей.