В основу типа object в С# положен класс object. Члены класса Object под робно рассматривались в главе 11, но поскольку он играет главную роль в С#, то его методы ради удобства повторно перечисляются в табл. 21.16. В классе object определен конструктор public Object()
который создает пустой объект.
Таблица 21.16. Методы, определенные в классе Object Класс Tuple
В версии .NET Framework 4.0 внедрен удобный способ создания групп объектов (так называемых кортежей). В основу этого способа положен статический класс Tuple, в котором определяется несколько вариантов метода Create() для создания корте жей, а также различные обобщенные классы типа Tuple<...>, в которых инкапсу лируются кортежи. В качестве примера ниже приведено объявление варианта метода Create(), возвращающего кортеж с тремя членами. Метод Назначение public virtual bool Equals(object obj) Возвращает логическое значение true, если вызывающий объект оказывается таким же, как и объект, определяемый параметром obj. В противном случае возвращается значение false public static bool Equals(object objA, object objВ) Возвращает логическое значение true, если объект objА оказывается таким же, как и объект objВ. В противном случае возвращается значение false protected Finalize() Выполняет завершающие действия перед процессом "сборки мусора". В C# метод Finalize() доступен через деструктор public virtual int GetHashCode() Возвращает хеш-код, связанный с вызывающим объектом public Type GetType() Получает тип объекта во время выполнения программы protected object MemberwiseClone() Создает "неполную" копию объекта. При этом копируются члены, но не объекты, на которые ссылаются эти члены public static bool ReferenceEquals(object objA, object objВ) Возвращает логическое значение true, если объекты objА и objВ ссылаются на один и тот же объект. В противном случае возвращается логическое значение false public virtual string ToString() Возвращает строку, описывающую объект public static Tuple<T1, T2, T3> Create<T1, T2, T3>(T1 item1, T2 item2, T3 item3)` Следует заметить, что данный метод возвращает объект типа Tuple<T1, Т2, Т3>, в котором инкапсулируются члены кортежа item1, item2 и item3. Вообще говоря, кортежи оказываются полезными в том случае, если группу значений нужно интерпретировать как единое целое. В частности, кортежи можно передавать методам, возвращать из методов или же сохранять в коллекции либо в массиве. Интерфейсы IComparable и IComparable
Во многих классах приходится реализовывать интерфейс IComparable или IComparable, поскольку он позволяет сравнивать один объект с другим, используя различные методы, определенные в среде .NET Framework. Интерфейсы IComparable и IComparable были представлены в главе 18, где они использовались в примерах программ для сравнения двух объектов, определяемых параметрами обобщенного типа. Кроме того, они упоминались при рассмотрении класса Array ранее в этой гла ве. Но поскольку эти интерфейсы имеют особое значение и применяются во многих случаях, то ниже приводится их краткое описание.
Интерфейс IComparable реализуется чрезвычайно просто, потому что он состоит всего лишь из одного метода. int CompareTo(object obj)
В этом методе значение вызывающего объекта сравнивается со значением объекта, определяемого параметром obj. Если значение вызывающего объекта больше, чем у объекта obj, то возвращается положительное значение; если оба значения равны — нулевое значение, а если значение вызывающего объекта меньше, чем у объекта obj, — отрицательное значение.
Обобщенный вариант интерфейса IComparable объявляется следующим об разом. public interface IComparable<T>
В данном варианте тип сравниваемых данных передается параметру Т в качестве ар гумента типа. В силу этого объявление метода CompareTo() претерпевает изменения и выглядит так, как показано ниже. int CompareTo(Т other)
В этом объявлении тип данных, которыми оперирует метод CompareTo(), может быть указан явным образом. Следовательно, интерфейс IComparable обеспечи вает типовую безопасность. Именно по этой причине он теперь считается более пред почтительным в программировании на С#, чем интерфейс IComparable. Интерфейс IEquatable
Интерфейс IEquatable<T> реализуется в тех классах, где требуется определить порядок сравнения двух объектов на равенство их значений. В этом интерфейсе опре делен только один метод, Equals(), объявление которого приведено ниже. bool Equals(T other)
Этот метод возвращает логическое значение true, если значение вызывающего объекта оказывается равным значению другого объекта other, в противном случае — логическое значение false.
Интерфейс IEquatable реализуется в нескольких классах и структурах среды .NET Framework, включая структуры числовых типов и класс String. Для реализа ции интерфейса IEquatable обычно требуется также переопределять методы Equals(Object) и GetHashCode(), определенные в классе Object. Интерфейс IConvertible
Интерфейс IConvertible реализуется в структурах всех типов значений, String и DateTime. В нем определяются различные преобразования типов. Реализовывать этот интерфейс в создаваемых пользователем классах, как правило, не требуется. Интерфейс ICloneable
Реализовав интерфейс ICloneable, можно создать все условия для копирования объекта. В интерфейсе ICloneable определен только один метод, Clone(), объявле ние которого приведено ниже. object Clone()
В этом методе создается копия вызывающего объекта, а конкретная его реализация зависит от способа создания копии объекта. Вообще говоря, существуют две разно видности копий объектов: полная и неполная. Если создается полная копия, то ко пия совершенно не зависит от оригинала. Так, если в исходном объекте содержится ссылка на другой объект О, то при его копировании создается также копия объекта О. А при создании неполной копии осуществляется копирование одних только членов, но не объектов, на которые эти члены ссылаются. Так, после создания неполной ко пии объекта, ссылающегося на другой объект О, копия и оригинал будут ссылаться на один и тот же объект О, причем любые изменения в объекте О будут оказывать влия ние как на копию, так и на оригинал. Как правило, метод Clone() реализуется для получения полной копии. А неполные копии могут быть созданы с помощью метода MemberwiseClone(), определенного в классе Object.
Ниже приведен пример программы, в которой демонстрируется применение ин терфейса ICloneable. В ней создается класс Test, содержащий ссылку на объект клас са X. В самом классе Test используется метод Clone() для создания полной копии. // Продемонстрировать применение интерфейса ICloneable. using System; class X { public int a; public X(int x) { a = x; } } class Test : ICloneable { public X о; public int b; public Test(int x, int y) { о = new X(x); b = y; } public void Show(string name) { Console.Write("Значения объекта " + name + ": "); Console.WriteLine("о.a: {0}, b: {1}", o.a, b); } // Создать полную копию вызывающего объекта. public object Clone() { Test temp = new Test(o.a, b); return temp; } } class CloneDemo { static void Main() { Test ob1 = new Test(10, 20); obi.Show("ob1"); Console.WriteLine("Сделать объект ob2 копией объекта оb1."); Test ob2 = (Test) ob1.Clone(); ob2.Show("ob2"); Console.WriteLine("Изменить значение оb1.о.а на 99, " + " а значение оb1.b - на 88."); оb1.о.а = 99; оb1.b = 88; оb1.Show("оb1"); ob2.Show("оb2"); } }
Ниже приведен результат выполнения этой программы. Значения объекта оb1: о.а: 10, b: 20 Сделать объект оb2 копией объекта оb1. Значения объекта оb2: о.а: 10, b: 20 Изменить значение оb1.о.а на 99, а значение оb1.b — на 88. Значения объекта оb1: о.а: 99, b: 88 Значения объекта оb2: о.а: 10, b: 20
Как следует из результата выполнения приведенной выше программы, объект оb2 является копией объекта оb1, но это совершенно разные объекты. Изменения в одном из них не оказывают никакого влияния на другой. Это достигается конструированием нового объекта типа Test, который выделяет новый объект типа X для копирования. При этом новому экземпляру объекта типа X присваивается такое же значение, как и у объекта типа X в оригинале.
Для получения неполной копии достаточно вызвать метод MemberwiseClone(), определяемый в классе Object из метода Clone(). В качестве упражнения попробуйте заменить метод Clone() в предыдущем примере программы на следующий его вариант. // Сделать неполную копию вызывающего объекта. public object Clone() { Test temp = (Test) MemberwiseClone(); return temp; }
После этого изменения результат выполнения данной программы будет выглядеть следующим образом. Значения объекта оb1: о.а: 10, b: 20 Сделать объект оb2 копией объекта оb1. Значения объекта оb2: о.а: 10, b: 20 Изменить значение оb1.о.а на 99, а значение оb1.b — на 88. Значения объекта оb1: о.а: 99, b: 88 Значения объекта оb2: о.а: 99, b: 20
Как видите, обе переменные экземпляра о в объектах оb1 и оb2 ссылаются на один и тот же объект типа X. Поэтому изменения в одном объекте оказывают влияние на другой. Но в то же время поля b типа int в каждом из них разделены, поскольку типы значений недоступны по ссылке. Интерфейсы IFormatProvider и IFormattable
В интерфейсе IFormatProvider определен единственный метод GetFormat(), ко торый возвращает объект, определяющий форматирование данных в удобочитаемой форме текстовой строки. Ниже приведена общая форма метода GetFormat(): object GetFormat(Type formatType)
где formatType — это объект, получаемый для форматирования. Интерфейс IFormattable поддерживает форматирование выводимых результатов в удобочитаемой форме. В нем определен следующий метод: string ToString(string format, IFormatProvider formatProvider)
где format обозначает инструкции для форматирования, a formatProvider — поставщик формата.
ПРИМЕЧАНИЕ Подробнее о форматировании речь пойдет в главе 22. Интерфейсы IObservable и IObserver
В версию .NET Framework 4.0 добавлены еще два интерфейса, поддерживающие шаблон наблюдателя: IObservable и IObserver. В шаблоне наблюдателя один класс (в роли наблюдаемого) предоставляет уведомления другому классу (в роли наблюдателя). С этой целью объект наблюдаемого класса регистрирует объект наблю дающего класса. Для регистрации наблюдателя вызывается метод Subscribe(), ко торый определен в интерфейсе IObservable и которому передается объект типа IObserver, принимающий уведомление. Для получения уведомлений можно зарегистрировать несколько наблюдателей. А для отправки уведомлений всем за регистрированным наблюдателям применяются три метода, определенные в интер фейсе IObserver. Так, метод OnNext() отправляет данные наблюдателю, метод OnError() сообщает об ошибке, а метод OnCompleted() указывает на то, что наблю даемый объект прекратил отправку уведомлений.