Полное руководство. С# 4.0 - Шилдт Герберт
Шрифт:
Интервал:
Закладка:
Для явной реализации интерфейсного метода могут быть две причины. Во-первых,когда интерфейсный метод реализуется с указанием его полного имени, то такой метод оказывается доступным не посредством объектов класса, реализующего данныйинтерфейс, а по интерфейсной ссылке. Следовательно, явная реализация позволяетреализовать интерфейсный метод таким образом, чтобы он не стал открытым членомкласса, предоставляющего его реализацию. И во-вторых, в одном классе могут бытьреализованы два интерфейса с методами, объявленными с одинаковыми именами исигнатурами. Но неоднозначность в данном случае устраняется благодаря указаниюв именах этих методов их соответствующих интерфейсов. Рассмотрим каждую из этихдвух возможностей явной реализации на конкретных примерах.
В приведенном ниже примере программы демонстрируется интерфейс IEven,в котором объявляются два метода: IsEven() и IsOdd(). В первом из них определяется четность числа, а во втором — его нечетность. Интерфейс IEven затем реализуетсяв классе MyClass. При этом метод IsOdd() реализуется явно.// Реализовать член интерфейса явно.using System;interface IEven { bool IsOdd(int x); bool IsEven(int x);}class MyClass : IEven { // Явная реализация. Обратите внимание на то, что // этот член является закрытым по умолчанию. bool IEven.IsOdd(int x) { if((x%2) != 0) return true; else return false; } // Обычная реализация, public bool IsEven(int x) { IEven о = this; // Интерфейсная ссылка на вызывающий объект. return !о.IsOdd(х); }}class Demo { static void Main() { MyClass ob = new MyClass(); bool result; result = ob.IsEven(4); if(result) Console.WriteLine("4 - четное число."); // result = ob.IsOdd(4); // Ошибка, член IsOdd интерфейса IEven недоступен // Но следующий код написан верно, поскольку в нем сначала создается // интерфейсная ссылка типа IEven на объект класса MyClass, а затем по // этой ссылке вызывается метод IsOdd(). IEven iRef = (IEven) ob; result = iRef.IsOdd(3); if(result) Console.WriteLine("3 — нечетное число."); }}
В приведенном выше примере метод IsOdd() реализуется явно, а значит, он недоступен как открытый член класса MyClass. Напротив, он доступен только по интерфейсной ссылке. Именно поэтому он вызывается посредством переменной о ссылочного типа IEven в реализации метода IsEven().
Ниже приведен пример программы, в которой реализуются два интерфейса, причем в обоих интерфейсах объявляется метод Meth(). Благодаря явной реализации исключается неоднозначность, характерная для подобной ситуации.// Воспользоваться явной реализацией для устранения неоднозначности.using System;interface IMyIF_A { int Meth(int x);}interface IMyIF_B { int Meth(int x);}// Оба интерфейса реализуются в классе MyClass.class MyClass : IMyIF_A, IMyIF_B { // Реализовать оба метода Meth() явно. int IMyIF_A.Meth(int x) { return x + x; } int IMyIF_B.Meth(int x) { return x * x; } // Вызывать метод Meth() по интерфейсной ссылке. public int MethA(int x) { IMyIF_A a_ob; a_ob = this; return a_ob.Meth(x); // вызов интерфейсного метода IMyIF_A } public int MethB(int x){ IMyIF_B b_ob; b_ob = this; return b_ob.Meth(x); // вызов интерфейсного метода IMyIF_B }}class FQIFNames { static void Main() { MyClass ob = new MyClass(); Console.Write("Вызов метода IMyIF_A.Meth(): "); Console.WriteLine(ob.MethA(3)); Console.Write("Вызов метода IMyIF_B.Meth(): "); Console.WriteLine(ob.MethB(3)); }}
Вот к какому результату приводит выполнение этой программы.Вызов метода IMyIF_A.Meth(): 6Вызов метода IMyIF_B.Meth(): 9
Анализируя приведенный выше пример программы, обратим прежде всего внимание на одинаковую сигнатуру метода Meth() в обоих интерфейсах, IMyIF_A иIMyIF_B. Когда оба этих интерфейса реализуются в классе MyClass, для каждого изних в отдельности это делается явно, т.е. с указанием полного имени метода Meth().А поскольку явно реализованный метод может вызываться только по интерфейснойссылке, то в классе MyClass создаются две такие ссылки: одна — для интерфейсаIMyIF_A, а другая — для интерфейса IMyIF_B. Именно по этим ссылкам происходитобращение к объектам данного класса с целью вызвать методы соответствующих интерфейсов, благодаря чему и устраняется неоднозначность.Выбор между интерфейсом и абстрактным классом
Одна из самых больших трудностей программирования на C# состоит в правильном выборе между интерфейсом и абстрактным классом в тех случаях, когда требуется описать функциональные возможности, но не реализацию. В подобных случаяхрекомендуется придерживаться следующего общего правила: если какое-то понятиеможно описать с точки зрения функционального назначения, не уточняя конкретныедетали реализации, то следует использовать интерфейс. А если требуются некоторыедетали реализации, то данное понятие следует представить абстрактным классом.Стандартные интерфейсы для среды .NET Framework
Для среды .NET Framework определено немало стандартных интерфейсов, которыми можно пользоваться в программах на С#. Так, в интерфейсе System.IComparableопределен метод CompareTo(), применяемый для сравнения объектов, когда требуется соблюдать отношение порядка. Стандартные интерфейсы являются также важной частью классов коллекций, предоставляющих различные средства, в том числестеки и очереди, для хранения целых групп объектов. Так, в интерфейсе System.Collections.ICollection определяются функции для всей коллекции, а в интерфейсе System.Collections.IEnumerator — способ последовательного обращенияк элементам коллекции. Эти и многие другие интерфейсы подробнее рассматриваются в части II данной книги.Структуры
Как вам должно быть уже известно, классы относятся к ссылочным типам данных.Это означает, что объекты конкретного класса доступны по ссылке, в отличие от значений простых типов, доступных непосредственно. Но иногда прямой доступ к объектамкак к значениям простых типов оказывается полезно иметь, например, ради повышения эффективности программы. Ведь каждый доступ к объектам (даже самым мелким)по ссылке связан с дополнительными издержками на расход вычислительных ресурсови оперативной памяти. Для разрешения подобных затруднений в C# предусмотренаструктура, которая подобна классу, но относится к типу значения, а не к ссылочномутипу данных.
Структуры объявляются с помощью ключевого слова struct и с точки зрения синтаксиса подобны классам. Ниже приведена общая форма объявления структуры:struct имя : интерфейсы { // объявления членов}
где имя обозначает конкретное имя структуры.
Одни структуры не могут наследовать другие структуры и классы или служитьв качестве базовых для других структур и классов. (Разумеется, структуры, как и всеостальные типы данных в С#, наследуют класс object.) Тем не менее в структуре можно реализовать один или несколько интерфейсов, которые указываются после имениструктуры списком через запятую. Как и у классов, у каждой структуры имеются своичлены: методы, поля, индексаторы, свойства, операторные методы и события. В структурах допускается также определять конструкторы, но не деструкторы. В то же времядля структуры нельзя определить конструктор, используемый по умолчанию (т.е. конструктор без параметров). Дело в том, что конструктор, вызываемый по умолчанию,определяется для всех структур автоматически и не подлежит изменению. Такой конструктор инициализирует поля структуры значениями, задаваемыми по умолчанию.А поскольку структуры не поддерживают наследование, то их члены нельзя указыватькак abstract, virtual или protected.
Объект структуры может быть создан с помощью оператора new таким же образом, как и объект класса, но в этом нет особой необходимости. Ведь когда используетсяоператор new, то вызывается конструктор, используемый по умолчанию. А когда этотоператор не используется, объект по-прежнему создается, хотя и не инициализируется.
В этом случае инициализацию любых членов структуры придется выполнить вручную.В приведенном ниже примере программы демонстрируется применение структуры для хранения информации о книге.// Продемонстрировать применение структуры.using System;// Определить структуру.struct Book { public string Author; public string Title; public int Copyright; public Book(string a, string t, int c) { Author = a; Title = t; Copyright = c; }}// Продемонстрировать применение структуры Book.class StructDemo { static void Main() { Book book1 = new Book("Герберт Шилдт", "Полный справочник пo C# 4.0", 2010) ; // вызов явно заданного конструктора Book book2 = new Book(); // вызов конструктора по умолчанию Book bоок3; // конструктор не вызывается Console.WriteLine(book1.Author + ", " + book1.Title + ", (c) " + book1.Copyright); Console.WriteLine(); if(book2.Title == null) Console.WriteLine("Член book2.Title пуст."); // А теперь ввести информацию в структуру book2. book2.Title = "О дивный новый мир"; book2.Author = "Олдос Хаксли"; book2.Copyright = 1932; Console.Write("Структура book2 теперь содержит:n"); Console.WriteLine(book2.Author + ", " + book2.Title + ", (c) " + book2.Copyright); Console.WriteLine(); // Console.WriteLine(bоокЗ.Title); // неверно, этот член структуры // нужно сначала инициализировать bоокЗ.Title = "Красный шторм"; Console.WriteLine(bоокЗ.Title); // теперь верно }}