Как видите, при реализации члена MyMeth () интерфейса IMylF указывается его полное имя, включающее в себя имя его интерфейса.

Для явной реализации интерфейсного метода могут быть две причины. Во-первых, когда интерфейсный метод реализуется с указанием его полного имени, то такой метод оказывается доступным не посредством объектов класса, реализующего данный интерфейс, а по интерфейсной ссылке. Следовательно, явная реализация позволяет реализовать интерфейсный метод таким образом, чтобы он не стал открытым членом класса, предоставляющего его реализацию. И во-вторых, в одном классе могут быть реализованы два интерфейса с методами, объявленными с одинаковыми именами и сигнатурами. Но неоднозначность в данном случае устраняется благодаря указанию в именах этих методов их соответствующих интерфейсов. Рассмотрим каждую из этих двух возможностей явной реализации на конкретных примерах.

В приведенном ниже примере программы демонстрируется интерфейс 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 (). Благодаря явной реализации исключается неоднозначность, характерная для подобной ситуации.

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 MyClassO;

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 имя : интерфейсы {

II объявления членов

}

где имя обозначает конкретное имя структуры.

Одни структуры не могут наследовать другие структуры и классы или служить в качестве базовых для других структур и классов. (Разумеется, структуры, как и все остальные типы данных в С#, наследуют класс obj ect.) Тем не менее в структуре можно реализовать один или несколько интерфейсов, которые указываются после имени структуры списком через запятую. Как и у классов, у каждой структуры имеются свои члены: методы, поля, индексаторы, свойства, операторные методы и события. В структурах допускается также определять конструкторы, но не деструкторы. В то же время для структуры нельзя определить конструктор, используемый по умолчанию (т.е. конструктор без параметров). Дело в том, что конструктор, вызываемый по умолчанию, определяется для всех структур автоматически и не подлежит изменению. Такой конструктор инициализирует поля структуры значениями, задаваемыми по умолчанию. А поскольку структуры не поддерживают наследование, то их члены нельзя указывать как abstract, virtual или protected.

Объект структуры может быть создан с помощью оператора new таким же образом, как и объект класса, но в этом нет особой необходимости. Ведь когда используется оператор new, то вызывается конструктор, используемый по умолчанию. А когда этот оператор не используется, объект по-прежнему создается, хотя и не инициализируется. В этом случае инициализацию любых членов структуры придется выполнить вручную.