Предотвращение наследования с помощью ключевого
⇐ ПредыдущаяСтр 8 из 8 Слова sealed Несмотря на всю эффективность и полезность наследования, иногда возникает потребность предотвратить его. Допустим, что имеется класс, инкапсулирующий последовательность инициализации некоторого специального оборудования, например медицинского монитора. В этом случае требуется, чтобы пользователи данного класса не могли изменять порядок инициализации монитора, чтобы исключить его неправильную настройку. Но независимо от конкретных причин в С# имеется возможность предотвратить наследование класса с помощью ключевого слова sealed. Для того чтобы предотвратить наследование класса, достаточно указать ключевое слово sealed перед определением класса. Как и следовало ожидать, класс не допускается объявлять одновременно как abstract и sealed, поскольку сам абстрактный класс реализован не полностью и опирается в этом отношении на свои производные классы, обеспечивающие полную реализацию. Ниже приведен пример объявления класса типа sealed. sealed class А { //... }
// Следующий класс недопустим. class В: А // ОШИБКА! Наследовать класс А нельзя { //... } Как следует из комментариев в приведенном выше фрагменте кода, класс В не может наследовать класс А, потому что последний объявлен как sealed. И еще одно замечание: ключевое слово sealed может быть также использовано в виртуальных методах для предотвращения их дальнейшего переопределения. Допустим, что имеется базовый класс В и производный класс D. Метод, объявленный в классе В как virtual, может быть объявлен в классе D как sealed. Благодаря этому в любом классе, наследующем от класса предотвращается переопределение данного метода. Подобная ситуация демонстрируется в приведенном ниже фрагменте кода.
Листинг 11.23 class B { public virtual void MyMethod() { /*... */ } }
class D: B { // Здесь герметизируется метод MyMethod() и // предотвращается его дальнейшее переопределение. sealed public override void MyMethod() { /*... */ } }
class X: D { // Ошибка! Метод MyMethod() герметизирован! public override void MyMethod() { /*... */ } } Метод MyMethod() герметизирован (запечатан) в классе D, и поэтому не может быть переопределен в классе X. Класс object В С# предусмотрен специальный класс object, который неявно считается базовым классом для всех остальных классов и типов, включая и типы значений. Иными словами, все остальные типы являются производными от object. Это, в частности, означает, что переменная ссылочного типа object может ссылаться на объект любого другого типа. Кроме того, переменная типа object может ссылаться на любой массив, поскольку в С# массивы реализуются как объекты. Формально имя object считается в С# еще одним обозначением класса System.Object, входящего в библиотеку классов для среды.NET Framework. В классе object определяются методы, приведенные в табл. 11.1. Это означает, что они доступны для каждого объекта. Некоторые из этих методов требуют дополнительных пояснений. По умолчанию метод Equals(object) определяет, ссылается ли вызывающий объект на тот же самый объект, что и объект, указываемый в качества аргумента этого метода, т.е. он определяет, являются ли обе ссылки одинаковыми. Метод Equals(object) возвращает логическое значение true, если сравниваемые объекты одинаковы, в противном случае — логическое значение false. Он может быть также переопределен в создаваемых классах. Это позволяет выяснить, что же означает равенство объектов для создаваемого класса. Например, метод Equals(object) можно определить таким образом, чтобы в нем сравнивалось содержимое двух объектов. Таблица 11.1 – Методы класса object
Метод GetHashCode() возвращает хеш-код, связанный с вызывающим объектом. Этот хеш-код можно затем использовать в любом алгоритме, где хеширование применяется в качестве средства доступа к хранимым объектам. Следует, однако, иметь в виду, что стандартная реализация метода GetHashCode() не пригодна на все случаи применения. Как известно, если перегружается оператор ==, то обычно приходится переопределять методы Equals(object) и GetHashCode(), поскольку чаще всего требуется, чтобы метод Equals(object) и оператор == функционировали одинаково. Когда же переопределяется метод Equals(object), то следует переопределить и метод GetHashCode(), чтобы оба метода оказались совместимыми. Метод ToString() возвращает символьную строку, содержащую описание того объекта, для которого он вызывается. Кроме того, метод ToString() автоматически вызывается при выводе содержимого объекта с помощью метода WriteLine(). Этот метод переопределяется во многих классах, что позволяет приспосабливать описание к конкретным типам объектов, создаваемых в этих классах. Ниже приведен пример применения данного метода. Листинг 11.24 // Продемонстрировать применение метода 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(ob1); Console.WriteLine(ob2); Console.WriteLine(ob3); } } При выполнении этого кода получается следующий результат.
Объект #0 типа MyClass Объект #1 типа MyClass Объект #2 типа MyClass
Упаковка и распаковка Как пояснялось выше, все типы в С#, включая и простые типы значений, являются производными от класса object. Следовательно, ссылкой типа object можно воспользоваться для обращения к любому другому типу, в том числе и к типам значений. Когда ссылка на объект класса object используется для обращения к типу значения, то такой процесс называется упаковкой. Упаковка приводит к тому, что значение простого типа сохраняется в экземпляре объекта, т.е. "упаковывается" в объекте, который затем используется как и любой другой объект. Но в любом случае упаковка происходит автоматически. Для этого достаточно присвоить значение переменной ссылочного типа object, а об остальном позаботится компилятор С#. Распаковка представляет собой процесс извлечения упакованного значения из объекта. Это делается с помощью явного приведения типа ссылки на объект класса object к соответствующему типу значения. Попытка распаковать объект в другой тип может привести к ошибке во время выполнения. Ниже приведен простой пример, демонстрирующий упаковку и распаковку. Листинг 11.25 // Простой пример упаковки и распаковки.
using System;
class BoxingDemo { static void Main() { int x; object obj;
x = 10; obj = x; // упаковать значение переменной x в объект
int y = (int)obj; // распаковать значение из объекта, доступного // по ссылке obj, в переменную типа int Console.WriteLine(y); } } В этом примере кода выводится значение 10. Обратите внимание на то, что значение переменной х упаковывается в объект простым его присваиванием переменной obj, ссылающейся на этот объект. А затем это значение извлекается из объекта, доступного по его ссылке obj, и далее приводится к типу int. Ниже приведен еще один, более интересный пример упаковки. В данном случае значение типа int передается в качестве аргумента методу Sqr(), который, в свою очередь, принимает параметр типа object. Листинг 11.26 // Пример упаковки при передаче значения методу.
using System;
class BoxingDemo { static void Main() { int x;
x = 10; Console.WriteLine("Значение x равно: " + x);
// Значение переменной x автоматически упаковывается // при передаче методу Sqr(). x = BoxingDemo.Sqr(x); Console.WriteLine("Значение x в квадрате равно: " + x); }
static int Sqr(object ob) { return (int)ob * (int)ob; } } В данном примере значение переменной х автоматически упаковывается при передаче методу Sqr(). Упаковка и распаковка позволяют полностью унифицировать систему типов в С#. Благодаря тому что все типы являются производными от класса object, ссылка на значение любого типа может быть просто присвоена переменной ссылочного типа object, а все остальное возьмут на себя упаковка и распаковка. Более того, методы класса object оказываются доступными всем типам, поскольку они являются производными от этого класса. В качестве примера рассмотрим довольно любопытную программу. Листинг 11.27 // Благодаря упаковке становится возможным вызов метода по значению!
using System;
class MethOnValue { static void Main() {
Console.WriteLine(10.ToString());
} } В результате выполнения этой программы выводится значение 10. Дело в том, что метод ToString() возвращает строковое представление объекта, для которого он вызывается. В данном случае строковым представлением значения 10 как вызывающего объекта является само значение 10!
Воспользуйтесь поиском по сайту: ©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...
|