Фактически это те же самые абстрактные классы, НЕ СОДЕРЖАЩИЕ объявлений данных – членов и объявлений ОБЫЧНЫХ функций.
Все без исключения функции-члены интерфейса – абстрактные. Поэтому интерфейс объявляется с особым ключевым словом interface, а функции интерфейса, несмотря на свою “абстрактность” объявляются без ключевого слова abstract.
Основное отличие интерфейса от абстрактного класса заключается в том, что производный класс может наследовать одновременно несколько интерфейсов.
using System;
namespace Interface01
{
// Интерфейсы.
// Этот интерфейс характеризуется уникальными
// именами объявленных в нём методов.
interface Ix
{
void IxF0(int xKey);
void IxF1();
}
// Пара интерфейсов, содержащих объявления одноимённых методов
// с одной и той же сигнатурой.
interface Iy
{
void F0(int xKey);
void F1();
}
interface Iz
{
void F0(int xKey);
void F1();
}
// А этому интерфейсу уделим особое внимание.
// Он содержит тот же набор методов, но в производном классе этот интерфейс
// будет реализован явным образом.
interface Iw
{
void F0(int xKey);
void F1();
}
// В классе TestClass наследуются интерфейсы...
class TestClass:Ix
,Iy
,Iz
,Iw
{
public int xVal;
// Конструкторы.
public TestClass()
{
xVal = 125;
}
public TestClass(int key)
{
xVal = key;
}
// Реализация функций интерфейса Ix.
// Этот интерфейс имеет специфические названия функций.
// В данном пространстве имён его реализация неявная и однозначная.
public void IxF0(int key)
{
xVal = key*5;
Console.WriteLine("IxF0({0})...", xVal);
}
public void IxF1()
{
xVal = xVal*5;
Console.WriteLine("IxF1({0})...", xVal);
}
// Реализация интерфейсов Iy и Iz в классе TestClass неразличима.
// Это неявная неоднозначная реализация интерфейсов.
// Однако, неважно, чью конкретно функцию реализуем. Оба интерфейса довольны...
public void F0(int xKey)
{
xVal = (int)xKey/5;
Console.WriteLine("(Iy/Iz)F0({0})...", xVal);
}
public void F1()
{
xVal = xVal/5;
Console.WriteLine("(Iy/Iz)F1({0})...", xVal);
}
// А это явная непосредственная реализация интерфейса Iw.
// Таким образом, класс TestClass содержит ТРИ варианта реализации функций интерфейсов
// с одной и той же сигнатутой. Два варианта реализации неразличимы. Третий
// (фактически второй) вариант реализации отличается квалифицированными именами.
void Iw.F0(int xKey)
{
xVal = xKey+5;
Console.WriteLine("Iw.F0({0})...", xVal);
}
void Iw.F1()
{
xVal = xVal-5;
Console.WriteLine("Iw.F1({0})...", xVal);
}
public void bF0()
{
Console.WriteLine("bF0()...");
}
}
class Class1
{
static void Main(string[] args)
{
TestClass x0 = new TestClass();
TestClass x1 = new TestClass(5);
x0.bF0();
// Эти методы представляют собой неявную ОДНОЗНАЧНУЮ реализацию
// интерфейса Ix.
x0.IxF0(10);
x1.IxF1();
// Эти методы представляют собой неявную НЕОДНОЗНАЧНУЮ реализацию
// интерфейсов Iy и Iz.
x0.F0(5);
x1.F1();
// А вот вызов функций с явным приведением к типу интерфейса.
// Собственный метод класса bF0() при подобных преобразованиях
// не виден.
(x0 as Iy).F0(7);
(x1 as Iz).F1();
// А теперь настраиваем ссылки различных типов интерфейсов
// на ОДИН И ТОТ ЖЕ объект-представитель класса TestClass.
// И через "призму" интерфейса всякий раз объект будет
Преимущества программирования с использованием интерфейсов проявляются в том случае, когда ОДНИ И ТЕ ЖЕ ИНТЕРФЕЙСЫ наследуются РАЗНЫМИ классами. И здесь всё определяется спецификой данной конкретной реализации.
using System;
namespace Interface02
{
// Объявляются два интерфейса, каждый из которых содержит объявление
// одноименного метода с единственным параметром соответствующего типа.
interface ICompare0
{
bool Eq(ICompare0 obj);
}
interface ICompare1
{
bool Eq(ICompare1 obj);
}
// Объявляются классы, наследующие оба интерфейса.
// В каждом из классов реализуются функции интерфейсов.
// В силу того, что объявленные в интерфейсах методы одноименные,
// в классах применяется явная реализация методов интерфейсов.