Интерфейсы - синтаксис, назначение, особенности
Интерфейс является «крайним случаем» абстрактного класса. В нем задается набор абстрактных методов, свойств и индексаторов, которые должны быть реализованы в производных классах. Иными словами, интерфейс определяет поведение,которое поддерживается реализующими этот интерфейс классами. Основная идеяиспользования интерфейса состоит в том, чтобы к объектам таких классов можнобыло обращаться одинаковым образом.
Каждый класс может определять элементы интерфейса по-своему. Так достигается полиморфизм: объекты разных классов по-разному реагируют на вызовыодного и того же метода.
Синтаксис интерфейса аналогичен синтаксису класса:
[ атрибуты ] [ спецификаторы ] interface иияинтерфейса [ : предки ]
тело_интерфейса [ : ]
Для интерфейса могут быть указаны спецификаторы new, public, protected, internal и private. Спецификатор new применяется для вложенных интерфейсов и имееттакой же смысл, как и соответствующий модификатор метода класса. Остальныеспецификаторы управляют видимостью интерфейса. В разных контекстах определенияинтерфейса допускаются разные спецификаторы. По умолчанию интерфейсдоступен только из сборки, в которой он описан (internal).
Интерфейс может наследовать свойства нескольких интерфейсов, в этом случае предки перечисляются через запятую. Тело интерфейса составляют абстрактныеметоды, шаблоны свойств и индексаторов, а также события.
Пример :
Interfae IExapmle
{
void Example();
int Function( int a);
int Property { get; }
}
Имя интерфейса всегда начинается с заглавной I . И понятно, что классы, которые унаследовали этот интерфейс должны полностью определять методы, которые в нем содержаться, при чем сигнатуры методов описанных в классе и в интерфейсе должны полностью совпадать.
Отличия интерфейса от абстрактного класса:
• элементы интерфейса по умолчанию имеют спецификатор доступа public и не могут иметь спецификаторов, заданных явным образом;
• интерфейс не может содержать полей и обычных методов — все элементы интерфейса должны быть абстрактными;
• класс, в списке предков которого задается интерфейс, должен определять все его элементы, в то время как потомок абстрактного класса может не переопределятьичасть абстрактных методов предка (в этом случае производный класс также будет абстрактным);
• класс может иметь в списке предков несколько интерфейсов, при этом они должен определять все их методы.
В списке предков класса сначала указывается его базовый класс, если он есть, а затем через запятую те интерфейсы, которые реализует этот класс Таким образом в С# поддерживается одиночное наследование для классов и множественное— для интерфейсов.Для реализуемых элементов интерфейса в классе следует указыватьспецификатор public.
Существует второй способ реализации интерфейса в классе: явное указание имени интерфейса перед реализуемым элементом. Спецификаторы доступа при этомне указываются. К таким элементам можно обращаться в программе только через объект типа интерфейса.
Class MyClass : IExample
{
Void IExample.Example();
}
…
IExample Ex = new MyClass();
Ex.Example();
//MyClassEx = newMyClass();
//Ex.Example(); error!!!!
***
При работе с объектом через объект типа интерфейса бывает необходимо убедиться, что объект поддерживает данный интерфейс. Проверка выполняется с помощью
бинарной операции is.
if ( объект is тип )
{
// выполнить преобразование "объекта" к "типу"
// выполнить действия с преобразованным объектом
}
***
Более эффективной является другая операция — as. Она выполняет преобразование к заданному типу, а если это невозможно, формирует результат null, например:
static void Act( object A )
{
IExampleEx= A as IExample;
if ( Ex != null ) Ex.Example();
}
10. Преобразование и приведение типов.
При вычислении выражений может возникнуть необходимость в преобразовании типов. Если операнды, входящие в выражение, одного типа и операция для этого типа определена, то результат выражения будет иметь тот же тип.
Если операнды разного типа и/или операция для этого типа не определена, перед вычислениями автоматически выполняется преобразование типа по правилам, обеспечивающим приведение более коротких типов к более длинным для сохранения значимости и точности. Автоматическое (неявное) преобразование возможно не всегда, а только если при этом не может случиться потеря значимости. Если неявного преобразования из одного типа в другой не существует, программист может задать явное преобразование типа с помощью операции (тип)х. Его результат остается на совести программиста.
!!!Арифметические операции не определены для более коротких, чем int, типов. Это означает, что если в выражении участвуют только величины типов sbyte, byte, short и ushort, перед выполнением операции они будут преобразованы в int. Таким образом, результат любой арифметической операции имеет тип не менее int
Если один из операндов имеет тип, изображенный на более низком уровне, чем другой, то он приводится к типу второго операнда при наличии пути между ними. Если пути нет, возникает ошибка компиляции. Если путей несколько, выбирается наиболее короткий, не содержащий пунктирных линий. Преобразование выполняется не последовательно, а непосредственно из исходного типа в результирующий.
Преобразование более коротких, чем int, типов выполняется при присваивании. Обратите внимание на то, что неявного преобразования из float и double в decimal не существует.
Преобразование из типов int, uint и long в тип float и из типа long в тип double может вызвать потери точности, но не потери значимости. В процессе других вариантовнеявного преобразования никакая информация не теряется.