Российский Государственный Технологический Университет им. К.Э. Циолковского
Опубликован: 02.03.2007 | Доступ: свободный | Студентов: 5636 / 790 | Оценка: 3.96 / 3.45 | Длительность: 27:04:00
ISBN: 978-5-9556-0086-4
Лекция 8:

Интерфейсы

< Лекция 7 || Лекция 8: 123 || Лекция 9 >
Аннотация: Интерфейсы фактически те же самые абстрактные классы, не содержащие объявлений данных-членов и объявлений обычных функций. О них рассказано в этой лекции.

Фактически это те же самые абстрактные классы, НЕ СОДЕРЖАЩИЕ объявлений данных-членов и объявлений ОБЫЧНЫХ функций.

Все без исключения функции — члены интерфейса – абстрактные. Поэтому интерфейс объявляется с особым ключевым словом 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.
// И через "призму" интерфейса всякий раз объект будет 
// выглядеть по-разному.
Console.WriteLine("==========Prism test==========");

Console.WriteLine("==========Ix==========");
Ix ix = x1;
ix.IxF0(5);
ix.IxF1();

Console.WriteLine("==========Iy==========");
Iy iy = x1;
iy.F0(5);
iy.F1();
Console.WriteLine("==========Iz==========");
Iz iz = x1;
iz.F0(5);
iz.F1();

Console.WriteLine("==========Iw==========");
Iw iw = x1;
iw.F0(10);
iw.F1();
}
}
}
Листинг 8.1.

Преимущества программирования с использованием интерфейсов проявляются в том случае, когда ОДНИ И ТЕ ЖЕ ИНТЕРФЕЙСЫ наследуются РАЗНЫМИ классами. При этом имеет место еще один вариант полиморфности: объект, представленный ссылкой-интерфейсом, способен проявлять различную функциональность в зависимости от реализации функций наследуемого интерфейса в классе данного объекта. И здесь все определяется спецификой данной конкретной реализации:

using System;

namespace Interface02
{
// Объявляются два интерфейса, каждый из которых содержит объявление
// одноименного метода с единственным параметром соответствующего типа.

interface ICompare0
{
bool Eq(ICompare0 obj);
} 

interface ICompare1
{
bool Eq(ICompare1 obj);
}
// Объявляются классы, наследующие оба интерфейса.
// В каждом из классов реализуются функции интерфейсов. 
// В силу того, что объявленные в интерфейсах методы – одноименные,
// в классах применяется явная реализация методов интерфейсов.
// Для классов-наследников интерфейсы представляют собой всего лишь
// базовые классы. Поэтому в соответствующих функциях сравнения
// допускается использование операции as.
//____________________________________________________________.  	
class C0:ICompare0,ICompare1
{

public int commonVal;
int valC0;
public C0(int commonKey, int key)
{
commonVal = commonKey;
valC0 = key;
}

// Метод реализуется для обеспечения сравнения объектов
// СТРОГО одного типа – C0.
bool ICompare0.Eq(ICompare0 obj)
{
C0 test = obj as C0;
if (test == null) return false;
if (this.valC0 == test.valC0)
	return true;
else
	return false;
}


// Метод реализуется для обеспечения сравнения объектов разного типа.
bool ICompare1.Eq(ICompare1 obj)
{
C1 test = obj as C1;
if (test == null) return false;
if (this.commonVal == test.commonVal)
	return true;
else
	return false;
}
}
//____________________________________________________________.  	

class C1:ICompare0,ICompare1
{
public int commonVal;
string valC1;

public C1(int commonKey, string key)
{
commonVal = commonKey;
valC1 = string.Copy(key);
}

// В классе C1 при реализации функции интерфейса ICompare0 реализован
// метод сравнения, который обеспечивает сравнение как объектов типа C1,
// так и объектов типа C0.
bool ICompare0.Eq(ICompare0 obj)
{
C1 test;

// Попытка приведения аргумента к типу C1.
// В случае успеха – сравнение объектов по специфическому признаку,
// который для данного класса представлен строковой переменной valC1.
// В случае неуспеха приведения
// (очевидно, что сравниваются объекты разного типа)
// предпринимается попытка по второму сценарию
// (сравнение объектов разных типов).
// Таким образом, в рамках метода, реализующего один интерфейс,
// используется метод второго интерфейса. Разумеется, при явном 
// приведении аргумента к типу второго интерфейса. 

test = obj as C1;
 if (test == null) 
	return ((ICompare1)this).Eq((ICompare1)obj);

if (this.valC1.Equals(test.valC1))
  return true;
else
return false;
}

// Метод реализуется для обеспечения сравнения объектов разного типа.
bool ICompare1.Eq(ICompare1 obj)
{
 C0 test = obj as C0;
 if (test == null) return false;
 if (this.commonVal == test.commonVal)
  return true;
 else
return false;
}
}

//===============================================================
// Место, где порождаются и сравниваются объекты.  
class Class1
{
static void Main(string[] args)
{
  C0 x1 = new C0(0,1);
  C0 x2 = new C0(1,1);
    
// В выражениях вызова функций - членов интерфейсов НЕ ТРЕБУЕТСЯ 
//	явного приведения значения параметра к типу интерфейса.
// Сравнение объектов - представителей одного класса (C0).
   if ((x1 as ICompare0).Eq(x2))
	 Console.WriteLine("Yes!");
   else                     
	 Console.WriteLine("No!");

  C1 y1 = new C1(0,"1");
   C1 y2 = new C1(1,"1");

// Сравнение объектов - представителей одного класса (C1).
   if ((y1 as ICompare0).Eq(y2))
	Console.WriteLine("Yes!");
   else                     
	Console.WriteLine("No!");

// Попытка сравнения объектов - представителей разных классов.
    if (((ICompare0)x1).Eq(y2))
	Console.WriteLine("Yes!");
    else                     
	Console.WriteLine("No!");


   if ((x1 as ICompare1).Eq(y2))
	Console.WriteLine("Yes!");
    else                     
	Console.WriteLine("No!");


   if (((ICompare1)y2).Eq(x2))
	Console.WriteLine("Yes!");
    else                     
	Console.WriteLine("No!");

// Здесь будет задействован метод сравнения, реализованный
// в классе C0 по интерфейсу ICompare0, который не является универсальным.
// Отрицательный результат может быть получен не только
// по причине неравенства значений сравниваемых величин,
// но и по причине несоответствия типов  операндов.
   Console.WriteLine("__________x2==y2__________");
   if ((x2 as ICompare0).Eq(y2))
	Console.WriteLine("Yes!");
   else                     
	Console.WriteLine("No!");

// А здесь вероятность положительного результата выше, поскольку в классе
// C1 метод интерфейса ICompare0 реализован как УНИВЕРСАЛЬНЫЙ.
// И это значит,
// что данный метод никогда не вернет отрицательного значения по причине
// несоответствия типов операндов. 
  Console.WriteLine("__________y2==x2__________");
   if ((y2 as ICompare0).Eq(x2))
	Console.WriteLine("Yes!");
   else                     
	Console.WriteLine("No!");
}
}
}
Листинг 8.2.

Реализация сортировки в массиве. Интерфейс IComparable

Независимо от типа образующих массив элементов, массив элементов данного типа – это особый класс, производный от базового класса Array.

Класс Array является основой для любого массива и для любого массива предоставляет стандартный набор методов для создания, манипулирования (преобразования), поиска и сортировки на множестве элементов массива.

В частности, варианты метода Sort() обеспечивают реализацию механизмов сортировки элементов ОДНОМЕРНОГО массива объектов (в смысле представителей класса Object ).

Сортировка элементов предполагает:

  • ПЕРЕБОР всего множества (или его части) элементов массива;
  • СРАВНЕНИЕ (значений) элементов массива в соответствии с определенным критерием сравнения.

При этом критерии для сравнения и алгоритмы сравнения могут задаваться либо с помощью массивов ключей, либо реализацией интерфейса IComparable, который специфицирует характеристики (тип возвращаемого значения, имя метода и список параметров) методов, обеспечивающих реализацию данного алгоритма. Далее будут рассмотрены различные варианты метода Sort.

  • Вариант метода Sort сортирует полное множество элементов одноразмерного массива с использованием алгоритма, который реализуется методами, специфицированными стандартным интерфейсом сравнения:
    public static void Sort(Array);

Реализованный на основе интерфейса алгоритм способен распознавать значения элементов массива и сравнивать эти элементы между собой, если это элементы предопределенных типов.

Таким образом, никаких проблем не существует, если надо сравнить и переупорядочить массивы элементов, для которых известно, КАК СРАВНИВАТЬ значения составляющих массив элементов. Относительно ..., System.Int16, System.Int32, System.Int64, ...,System.Double,... всегда можно сказать, какой из сравниваемых элементов больше, а какой меньше.

  • Сортировка элементов массива (массива целевых элементов) также может быть произведена с помощью вспомогательного массива ключей:
    public static void Sort(Array, Array);
< Лекция 7 || Лекция 8: 123 || Лекция 9 >
kewezok kewezok
kewezok kewezok
Елена Шляхт
Елена Шляхт
Объясните плиз в чем отличие а++ от ++а
Почему результат разный?
int a=0, b=0;
Console.WriteLine(a++); //0
Console.WriteLine(++b); //1
a++;
++b;
Console.WriteLine(a); //2
Console.WriteLine(b); //2