Полное руководство. С# 4.0 - Шилдт Герберт
Шрифт:
Интервал:
Закладка:
Как показывает представленный выше пример программы, во всех производныхклассах метод Area() должен быть непременно переопределен, а также объявлен абстрактным. Убедитесь в этом сами, попробовав создать производный класс, в которомне переопределен метод Area(). В итоге вы получите сообщение об ошибке во времякомпиляции. Конечно, возможность создавать ссылки на объекты типа TwoDShape по-прежнему существует, и это было сделано в приведенном выше примере программы,но объявлять объекты типа TwoDShape уже нельзя. Именно поэтому массив shapesсокращен в методе Main() до 4 элементов, а объект типа TwoDShape для общей двухмерной формы больше не создается.
Обратите также внимание на то, что в класс TwoDShape по-прежнему входит методShowDim() и что он не объявляется с модификатором abstract. В абстрактные классы вполне допускается (и часто практикуется) включать конкретные методы, которыемогут быть использованы в своем исходном виде в производном классе. А переопределению в производных классах подлежат только те методы, которые объявлены какabstract.Предотвращение наследования с помощью ключевого слова sealed
Несмотря на всю эффективность и полезность наследования, иногда возникает потребность предотвратить его. Допустим, что имеется класс, инкапсулирующий последовательность инициализации некоторого специального оборудования, напримермедицинского монитора. В этом случае требуется, чтобы пользователи данного классане могли изменять порядок инициализации монитора, чтобы исключить его неправильную настройку. Но независимо от конкретных причин в C# имеется возможностьпредотвратить наследование класса с помощью ключевого слова sealed.
Для того чтобы предотвратить наследование класса, достаточно указать ключевоеслово sealed перед определением класса. Как и следовало ожидать, класс не допускается объявлять одновременно как abstract и sealed, поскольку сам абстрактныйкласс реализован не полностью и опирается в этом отношении на свои производныеклассы, обеспечивающие полную реализацию.
Ниже приведен пример объявления класса типа sealed.sealed class А { // ...}// Следующий класс недопустим.class В : A ( // ОШИБКА! Наследовать класс А нельзя // ...}
Как следует из комментариев в приведенном выше фрагменте кода, класс В не может наследовать класс А, потому что последний объявлен как sealed.
И еще одно замечание: ключевое слово sealed может быть также использованов виртуальных методах для предотвращения их дальнейшего переопределения. Допустим, что имеется базовый класс В и производный класс D. Метод, объявленныйв классе В как virtual, может быть объявлен в классе D как sealed. Благодаря этомув любом классе, наследующем от класса предотвращается переопределение данногометода. Подобная ситуация демонстрируется в приведенном ниже фрагменте кода.class В { public virtual void MyMethod() { /* ... */ }}class D : В { // Здесь герметизируется метод MyMethod() и // предотвращается его дальнейшее переопределение. sealed public override void MyMethod() { /* ... */ }}class X : D { // Ошибка! Метод MyMethodO герметизирован! public override void MyMethod() { /* ... */ }}
Метод MyMethod() герметизирован в классе D, и поэтому не может быть переопределен в классе X.Класс object
В C# предусмотрен специальный класс object, который неявно считается базовымклассом для всех остальных классов и типов, включая и типы значений. Иными словами, все остальные типы являются производными от object. Это, в частности, означает,что переменная ссылочного типа object может ссылаться на объект любого другоготипа. Кроме того, переменная типа object может ссылаться на любой массив, поскольку в C# массивы реализуются как объекты. Формально имя object считается вC# еще одним обозначением класса System.Object, входящего в библиотеку классовдля среды .NET Framework.
В классе object определяются методы, приведенные в табл. 11.1. Это означает, чтоони доступны для каждого объекта.
Некоторые из этих методов требуют дополнительных пояснений. По умолчаниюметод Equals(object) определяет, ссылается ли вызывающий объект на тот же самый объект, что и объект, указываемый в качества аргумента этого метода, т.е. он определяет, являются ли обе ссылки одинаковыми. Метод Equals(object) возвращаетлогическое значение true, если сравниваемые объекты одинаковы, в противном случае — логическое значение false. Он может быть также переопределен в создаваемыхклассах. Это позволяет выяснить, что же означает равенство объектов для создаваемогокласса. Например, метод Equals(object) можно определить таким образом, чтобыв нем сравнивалось содержимое двух объектов.
Таблица 11.1. Методы класса objectМетодНазначениеpublic virtual bool Equals(object ob)Определяет, является ли вызывающий объект таким же, как и объект, доступный по ссылке obpublic static bool Equals(object objA, object objB)Определяет, является ли объект, доступный по ссылке objA, таким же, как и объект, доступный по ссылке objBprotected Finalize()Выполняет завершающие действия перед "сборкой мусора”. В C# метод Finalize() доступен посредством деструктораpublic virtual int GetHashCode()Возвращает хеш-код, связанный с вызывающим объектомpublic Type GetType()Получает тип объекта во время выполнения программыprotected object MemberwiseClone()Выполняет неполное копирование объекта, т.е. копируются только члены, но не объекты, на которые ссылаются эти членыpublic static boolReferenceEquals(obj objA, object objB)Определяет, делаются ли ссылки objA и objB на один и тот же объектpublic virtual string ToString()Возвращает строку, которая описывает объект
Метод GetHashCode() возвращает хеш-код, связанный с вызывающим объектом.Этот хеш-код можно затем использовать в любом алгоритме, где хеширование применяется в качестве средства доступа к хранимым объектам. Следует, однако, иметь ввиду, что стандартная реализация метода GetHashCode() не пригодна на все случаиприменения.
Как упоминалось в главе 9, если перегружается оператор ==, то обычно приходится переопределять методы Equals(object) и GetHashCode(), поскольку чаще всеготребуется, чтобы метод Equals(object) и оператор == функционировали одинаково.Когда же переопределяется метод Equals(object), то следует переопределить и метод GetHashCode(), чтобы оба метода оказались совместимыми.
Метод ToString() возвращает символьную строку, содержащую описание тогообъекта, для которого он вызывается. Кроме того, метод ToString() автоматическивызывается при выводе содержимого объекта с помощью метода WriteLine(). Этотметод переопределяется во многих классах, что позволяет приспосабливать описаниек конкретным типам объектов, создаваемых в этих классах. Ниже приведен примерприменения данного метода.// Продемонстрировать применение метода ToString()using System;class MyClass { static int count = 0; int id; public MyClass() { id = count; count++; } public override string ToString() { return "Объект #" + id + " типа MyClass"; }}class Test { static void Main() { MyClass ob1 = new MyClass(); MyClass ob2 = new MyClass(); MyClass ob3 = new MyClass(); Console.WriteLine(obi); Console.WriteLine(ob2); Console.WriteLine(ob3); }}
При выполнении этого кода получается следующий результат.Объект #0 типа MyClassОбъект #1 типа MyClassОбъект #2 типа MyClassУпаковка и распаковка
Как пояснялось выше, все типы в С#, включая и простые типы значений, являютсяпроизводными от класса object. Следовательно, ссылкой типа object можно воспользоваться для обращения к любому другому типу, в том числе и к типам значений.Когда ссылка на объект класса object используется для обращения к типу значения,то такой процесс называется упаковкой. Упаковка приводит к тому, что значение простого типа сохраняется в экземпляре объекта, т.е. "упаковывается" в объекте, которыйзатем используется как и любой другой объект. Но в любом случае упаковка происходит автоматически. Для этого достаточно присвоить значение переменной ссылочноготипа object, а об остальном позаботится компилятор С#.