Страницы

Поиск по вопросам

понедельник, 12 ноября 2018 г.

Интерфейс, тип object, упаковка интерфейса, перечисление

Всем привет. Решил прочесть Рихтера (после Шилдта) и попал в путаницу. Насколько я раньше знал, то интерфейс и enum не наследуются от обжекта, однако после такого вот кода я запутался
IComparable F; F.ToString(); // или любой другой метод object, написал для демонстрации Console.WriteLine(typeof(IComparable).BaseType) //пустая строка
Путаница в том, что во-первых откуда у интерфейса и enum взялись встроенные методы object ? А во-вторых, я уж было смирился с этим, однако последняя строка выводит пустой экран вместо имени базового класса. Как это можно понять, подскажите пожалуйста, если учесть, что якобы интерфейс наследуется от обжекта ?
Далее встретил у него в книге "CLR VIA C# 4.5" стр 167 (предпоследний блок кода примера) фразу в этом коде
using System; internal struct Point : IComparable { private Int32 m_x, m_y; // Конструктор, просто инициализирующий поля public Point(Int32 x, Int32 y) { m_x = x; m_y = y; } // Переопределяем метод ToString, унаследованный от System.ValueType public override String ToString() { // Возвращаем Point как строку (вызов ToString предотвращает упаковку) return String.Format("({0}, {1})", m_x.ToString(), m_y.ToString()); } // Безопасная в отношении типов реализация метода CompareTo public Int32 CompareTo(Point other) { // Используем теорему Пифагора для определения точки, // наиболее удаленной от начала координат (0, 0) return Math.Sign(Math.Sqrt(m_x * m_x + m_y * m_y) - Math.Sqrt(other.m_x * other.m_x + other.m_y * other.m_y)); } // Реализация метода CompareTo интерфейса IComparable public Int32 CompareTo(Object o) { if (GetType() != o.GetType()) throw new ArgumentException("o is not a Point");      // Вызов безопасного в отношении типов метода CompareTo return CompareTo((Point)o); } } public static class Program { public static void Main() { // Создаем в стеке два экземпляра Point Point p1 = new Point(10, 10); Point p2 = new Point(20, 20); // p1 НЕ пакуется для вызова ToString (виртуальный метод) Console.WriteLine(p1.ToString()); // "(10, 10)" // p1 ПАКУЕТСЯ для вызова GetType (невиртуальный метод) Console.WriteLine(p1.GetType()); // "Point" // p1 НЕ пакуется для вызова CompareTo // p2 НЕ пакуется, потому что вызван CompareTo(Point) Console.WriteLine(p1.CompareTo(p2)); // "-1" // p1 ПАКУЕТСЯ, а ссылка размещается в c IComparable c = p1; Console.WriteLine(c.GetType()); // "Point" // p1 НЕ пакуется для вызова CompareTo // Поскольку в CompareTo не передается переменная Point, // вызывается CompareTo(Object), которому нужна ссылка // на упакованный Point // c НЕ пакуется, потому что уже ссылается на упакованный Point Console.WriteLine(p1.CompareTo(c)); // "0" // c НЕ пакуется, потому что уже ссылается на упакованный Point // p2 ПАКУЕТСЯ, потому что вызывается CompareTo(Object) Console.WriteLine(c.CompareTo(p2));// "-1" // c пакуется, а поля копируются в p2 p2 = (Point)c; // Убеждаемся, что поля скопированы в p2 Console.WriteLine(p2.ToString());// "(10, 10)" } }
// c пакуется, а поля копируются в p2 однако как он может паковаться, если он ссылается на уже запакованную структуру ?? Ведь когда интерфейсу присваиваешь тип значение то происходит упаковка (ранее в коде интерфейсу присвоили тип значение)


Ответ

Интерфейс действительно не наследуются от object. Строго говоря, интерфейсы - это не совсем типы. Вот что об этом говорит спецификация (пункт 3.4.5):
The members of an interface are the members declared in the interface and in all base interfaces of the interface. The members in class object are not, strictly speaking, members of any interface (§13.2). However, the members in class object are available via member lookup in any interface type
Иными словами, если то же самое сказать языком Бродского и Достоевского, то интерфейсы не наследуются от object, однако компилятор любезно позволяет нам использовать члены object'a без прямого приведения типа. Наверное просто ради удобства, так как в C# все так или иначе является объектом, а потому имеет ссылки на эти методы
Что же касается enum'ов, то тут вы не правы. Они как раз наследуются от object опосредованно через System.ValueType, о чем нам также любезно сообщает спецификация (п. 3.4.3):
The members of an enumeration are the constants declared in the enumeration and the members inherited from the enumeration’s direct base class System.Enum and the indirect base classes System.ValueType and object
Что же касается второй части вопроса, про упаковку-распаковку, то здесь скорее всего имеет место ошибка перевода или же самой книги. В строке, о которой вы говорите, происходит распаковка (то есть преобразование экземпляра ссылочного типа к типу-значению), в чем легко убедиться, посмотрев IL-код. Для такого C#-кода
Point pt = new Point(); IComparable a = pt; Point p2 = (Point) a;
соответствующий IL будет примерно таков:
ldloca.s pt initobj MyNamespace.Point ldloc.0 box MyNamespace.Point stloc.1 ldloc.1 unbox.any MyNamespace.Point pop ret
как можно видеть, операция упаковки (box) только одна, и за ней следует распаковка (unbox)

Комментариев нет:

Отправить комментарий