Полное руководство. С# 4.0
Шрифт:
При реализации члена интерфейса имеется возможность указать его имя полно стью вместе с именем самого интерфейса. В этом случае получается явная реализация члена интерфейса, или просто явная реализация. Так, если объявлен интерфейс IMyIF interface IMyIF { int MyMeth(int x); }
то следующая его реализация считается вполне допустимой: class MyClass : IMyIF { int IMyIF.MyMeth(int x) { return x / 3; } }
Как видите, при реализации члена MyMeth интерфейса IMyIF указывается его полное имя, включающее в себя имя его интерфейса.
Для явной реализации интерфейсного метода могут быть две причины. Во-первых, когда интерфейсный метод реализуется с указанием его полного имени, то такой ме тод оказывается доступным не посредством объектов класса, реализующего данный интерфейс, а по интерфейсной ссылке. Следовательно, явная реализация позволяет реализовать интерфейсный метод
В приведенном ниже примере программы демонстрируется интерфейс 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); // теперь верно } }