Интерфейсы в C#
Троелсен стр.247
Для начала давайте ознакомимся с формальным определением интерфейсного типа.
Интерфейс представляет собой просто именованный набор абстрактных членов. Как
упоминалось в главе 6, абстрактные методы являются чистым протоколом, поскольку
они не предоставляют стандартной реализации. Специфичные члены, определяемые
интерфейсом, зависят от того, какое точно поведение он моделирует. Другими словами,
интерфейс выражает поведение, которое заданный класс или структура может избрать
для поддержки. Более того, как будет показано далее в этой главе, класс или структура
может поддерживать столько интерфейсов, сколько необходимо, и, следовательно, тем
самым поддерживать множество поведений.
Троелсен стр. 301 (добавить)
Методы, помеченные как abstract, являются чистым протоколом. Они просто определяют имя, возвращаемый тип (если есть) и набор параметров (при необходимти).
Здесь абстрактный класс Shape информирует типы-наследники о том, что у него имеется метод по имени Draw (), который не принимает аргументов и ничего не возвращает. О необходимых деталях должен позаботиться наследник.
Троелсен стр. 288
интерфейсный тип может показаться очень похожим на абстрактный базовый класс. Вспомните, что когда класс помечается как абстрактный, он может определять любое количество абстрактных членов для предоставления полиморфного интерфейса всем производным типам. Однако даже если класс действительно определяет набор абстрактных членов, он также может определять любое количество конструкторов, полей данных, неабстрактных членов (с реализацией) и т.п. С другой стороны, интерфейсы могут содержать только определения абстрактных членов.
Полиморфный интерфейс, устанавливаемый абстрактным родительским классом, обладает одним серьезным ограничением: члены, определенные абстрактным родительским классом, поддерживаются только производными типами. Тем не менее, в крупных программных системах очень часто разрабатываются многочисленные иерархии классов, не имеющие общего родителя за исключением System.Object. Учитывая, что абстрактные члены в абстрактном базовом классе применимь только к производным типам, не существует никакого способа настройки типов в разных иерархиях на поддержку одного и того же полиморфного интерфейса.
Интерфейсом называется класс, содержащий набор абстрактных методов для реализации некоторых действий, обычно, вспомогательных по отношению к основной операции. Абстрактные методы, по определению, реализации не имеют. Они требуют явного переопределения. Применение стандартных интерфейсов предоставляет пользователю полный набор методов для реализации требуемого действия. С другой стороны, компилятор обеспечивает частичную реализацию абстрактных методов.
Интерфейсы бывают обобщенные и необобщенные. В данном разделе рассмотрены необобщенные интерфейсы. Обобщенные интерфейсы (< >) будут рассмотрены далее после обобщений вместе с коллекциями.
Рассмотрим интерфейсы, например, для реализации операции сравнения. Сравнить два числа можно, написав логическое отношение, скажем: a > b. Ситуация изменяется, если требуется сравнить два объекта, обладающих рядом атрибутов, которые должны учитываться при сравнении по одному или в некоторой взаимосвязи друг с другом. Характерным примером является выбор для товара лучшего соотношения цены и качества, причем качество, как правило, определяется набор свойств.
Реализации сравнения помогают интерфейсы IComparable и IComparator. (В языке C# принято имена интерфейсов начинать с буквы I). Первый из них задает возможность сравнения текущего объекта (описанного в классе, который наследует интерфейс IComparable) с заданным объектом такого же типа. Аналогия с переменными: сравнение текущего значения переменной с константой. Интерфейс IComparator позволяет сравнивать два независимых объекта. Аналогия с переменными: сравнение двух переменных.
Интерфейс IComparable имеет абстрактный метод CompareTo, имеющий формат:
public int CompareTo(<имя класса> obj) // Пример записи
В примере ниже при оформлении метода записан параметр общего класса object, от которого наследуются все классы. Затем объект этого класса преобразуется в локальный объект основного класса (b = (shirt) obj;). Это сделано для того, чтобы можно было сравнивать два соседних элемента в списке, один из которых текущий (слева от знака равенства), а другой следующий в списке (передается как параметр). (Одна из задач сравнения – выполнить сортировку).
Метод CompareTo возвращает целое число, указывающее на соотношение между объектами, как указано в таблице. Это число указывает на порядок следования объектов друг за другом, позволяя гибко устанавливать порядок сортировки:
Возвращаемое число Смысл
Меньше нуля Данный объект меньше объекта, заданного в виде параметра.
Нуль Данный объект и объект, заданный в виде параметра, равны.
Больше нуля. Данный объект больше объекта, заданного в виде параметра.
Изменяя порядок цифр можно изменить порядок сортировки.
Ниже приведена программа, в которой формируется список рубашек. Для рубашки задано всего 3 атрибута: фасон, размер и цена. Листинг программы для реализации сортировки рубашек по цене.
// Программа для иллюстрации необобщенного интерфейса IComparable.
class shirt : IComparable
{
int size; // Размер
string fashion; // Фасон
int price; // Цена
public shirt( string f, int p, int s) // Конструктор с параметрами
{
public size = s { get; set; }
public fashion = f { get; set; }
public price = p { get; set; }
}
// Реализация метода CompareTo для интерфейса IComparable.
public int CompareTo(object obj)
{
shirt b; // Объявление локального объекта
b = (shirt) obj; // Приведение типа
// b = obj as shirt; Можно и так
int n = price.CompareTo(b.price); // Выполнение сравнения
return n;
}
}
class IComparableDemo
{
static void Main()
{
ArrayList sh = new ArrayList(); // Объявление пустого списка
// Формирование списка.
sh.Add(new shirt("Libera Vita", 2250, 44));
sh.Add(new shirt("Broadway", 1760, 42));
sh.Add(new shirt("Sela", 1000, 48));
sh.Add(new shirt("Selected Homme", 2570, 48));
Console.WriteLine("Список рубашек до сортировки:");
foreach (shirt ob in sh)
Console.WriteLine("Фасон {0,-15} Стоимость: {1,4:D} Размер: {2}",
ob.Fash, ob.Price, ob.Size);
// Сортировка списка с помощью встроенного метода. В методе
// CompareTo задан член класса для сравнения
sh.Sort();
Console.WriteLine("
Список рубашек после сортировки по цене:");
foreach (shirt ob in sh)
Console.WriteLine("Фасон {0,-15} Стоимость: {1,4:D} Размер: {2}",
ob.Fash, ob.Price, ob.Size);
Console.ReadKey();
}
}
Ниже приведен результат выполнения программы.
Если в методе CompareTo заменить строку выполнения сравнения:
int n = price.CompareTo(b.price); // Выполнение сравнения
строкой, приведенной ниже (а также заменить текст заголовка таблицы после сортировки), то произойдет сортировка по фасону
int n = fashion.CompareTo(b.fashion);
Это пример, когда метод сравнения встроен в обрабатываемый класс.
Результат будет таким:
Способы выбора режима сортировки во время выполнения программы будут рассмотрены далее.
В отличие от IComparable, интерфейс IComparer обычно реализуется не в самом подлежащем сортировке типе (Product в рассматриваемом случае), а в наборе соответствующих вспомогательных классов, по одному для каждого порядка сортировки (по дружественному названию, идентификатору и т.д.). В настоящее время типу Product (товар) уже "известно", как ему следует сравнивать себя с другими товарами по внутреннему идентификатору. Следовательно, чтобы позволить пользователю объекта производить сортировку массива объектов Product еще и по значению Price, понадобится создать дополнительный вспомогательный класс, реализующий IComparer.
Для сравнения можно применять обобщенные интерфейсы IComparable<T> и IComparer<T>. Как следует из названия, интерфейс IComparable<T> предусматривает возможность выполнения сравнения. Интерфейс IComparable<T>, когда T определен, позволяет сравнивать текущий объект (объект объявлен в том же классе (член того класса), в котором выполняется сравнение) с другим объектом того же типа. Интерфейс IComparer<T> (дословно «сравниватель») используется для сравнения двух объектов T. Таким образом, объявление:
class MyClass : IComparable<T>
Вряд ли имеет смысл, поскольку MyClass и T не связаны друг с другом. С другой стороны запись ниже показывает, как это следует делать:
class T : IComparable<T>
{
public int CompareTo(T other) // Сравнить с другим
{
// Операторы для выполнения сравнения...
}
}
Интерфейс IComparer<T> может быть полезен, когда требуется выполнить сортировку, задавая определенный пользователем порядок следования объектов. Например, класс Product можно сортировать по цене (основной порядок следования элементов), но можно сортировать и по названию товара. Это можно выполнить так:
public class Product: IComparable <Product>
{
public string Name { get; set; }
public int Price { get; set; }
public Product() { } //
// Реализация метода CompareTo (по цене) для интерфейса IComparable.
public int CompareTo(Product obj)
{
Product b; // Объявление локального объекта
b = (Product)obj; // Приведение типа
int n = Price.CompareTo(b.Price); // Выполнение сравнения
return n;
}
}
// Сравнение товаров по названию (класс задает сравнение по названию)
// имя NameComparer может быть любым
public class NameComparer: IComparer<Product>
{
// Вызов общего метода сравнения
public int Compare(Product a, Product b)
{
if (a.Name.CompareTo(b.Name) != 0)
{// Переключение метода сравнения CompareTo на название товара
return a.Name.CompareTo(b.Name);
}
else return 0;
}
}
class Program
{
static void Main(string[] args)
{
List<Product> lstProd = new List<Product>()
{
new Product{Name="Пицца",Price=83},
new Product{Name="Багет", Price=65},
new Product{Name="Пирог",Price=35},
new Product{Name="Орбит",Price=25},
new Product{Name="Фанта",Price=48},
new Product{Name="Пепси",Price=88}
};
Console.WriteLine("Исходный массив");
foreach (Product prod in lstProd)
Console.WriteLine("{0} {1}",prod.Name, prod.Price);
// Сортировка по умолчанию, определено методом CompareTo
lstProd.Sort();
Console.WriteLine("
После сортировки по цене");
foreach (Product prod in lstProd)
Console.WriteLine("{0} {1}",prod.Name, prod.Price);
// Изменение порядка сортировки интерфейсом IComparer
Console.WriteLine("
После сортировки по названию");
NameComparer pc = new NameComparer();
lstProd.Sort();
foreach (Product prod in lstProd)
Console.WriteLine("{0} {1}",prod.Name, prod.Price);
Console.ReadKey();
}
}
Результат выполнения программы
Обратите внимание, для изменения порядка сортировки был добавлен класс, в котором было задано другое поле для сортировки.
Для выполнения сортировки по разным полям достаточно одного интерфейса IComparable <T>, а для изменения порядка сортировки достаточно вставить классы, наследующие интерфейс IComparer<T>, каждый из которых будет задавать свой порядок сортировки.
Интерфейсы IComparer<T> и IComparable<T> являются точными аналогами интерфейсов IEqualityComparer<T> и IEquatable<T>, но эти интерфейсы применяются для проверки на равенство, а не для установки порядка следования элементов.
Интерфейсы в C#
Лекции по предмету «Программирование»