Опубликован: 02.12.2009 | Уровень: специалист | Доступ: платный | ВУЗ: Тверской государственный университет
Лекция 6:

Интерфейсы. Множественное наследование

< Лекция 5 || Лекция 6: 12345 || Лекция 7 >

Интерфейсы и поля

Можно ли в интерфейсе объявлять не только методы, но и поля, обязательные для реализации потомками интерфейса? Ответ не однозначный: и да, и нет. Нет - потому что в явном виде в интерфейсе нельзя объявить поле. Да - потому что в интерфейсе можно объявить метод-свойство с процедурами get и set, обеспечивающими доступ к полю.

Вот пример такого интерфейса:

/// <summary>
    /// Доступ к полям Name и Age 
    /// </summary>
    interface IFields
    {
        string Name { get; set; }
        int Age {get;}
    }

Создадим класс, наследующий этот интерфейс:

/// <summary>
    /// Класс, наследующий интерфейс IFields
    /// Имеет поля Name и Age,
    /// доступ к полю Name открыт клиентам класса,
    /// доступ к полю Age закрыт и открыт с переименованием!  
    /// </summary>
    class TwoFields:IFields
    {
        string name;
        int age;
        public TwoFields()
        {
            name = "Nemo"; age = 37;
        }
        public TwoFields(string name, int age)
        {
            this.name = name; this.age = age;
        }
        public string Name
        {
            get { return name; }
            set { name = value; }
        }
        int IFields.Age
        {
            get { return age; }
        }
        public int WhatAge()
        {
            return age;
        }
    }

Обратите внимание: методы доступа к полям, наследуемые от интерфейса, можно реализовать как открытые и как закрытые, открывая их затем под другим именем. У класса всегда должна быть возможность переименовывать имена методов и полей, наследуемых от интерфейса.

Добавим в класс Testing новый метод, позволяющий протестировать работу с наследуемыми полями.

public void TestFields()
  {
      Console.WriteLine("Работа с объектом класса TwoFields!");
      TwoFields twofields = new TwoFields();
      Console.WriteLine("Имя: {0}, Возраст: {1}",
          twofields.Name, twofields.WhatAge());
      twofields.Name = "Captain Nemo";
      Console.WriteLine("Работа с интерфейсным объектом  IFields!");
      IFields ifields = (IFields)twofields;
      Console.WriteLine("Имя: {0}, Возраст: {1}",
          ifields.Name,ifields.Age);
  }

Результаты работы теста показаны на рис. 5.5.

Поля, наследуемые от интерфейса IFields

Рис. 5.5. Поля, наследуемые от интерфейса IFields

Встроенные интерфейсы

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

Упорядоченность объектов и интерфейс IComparable

Часто, когда создается класс, желательно задать отношение порядка на его объектах. Такой класс следует объявить наследником интерфейса IComparable. Этот интерфейс имеет всего один метод CompareTo (object obj), возвращающий целочисленное значение, положительное, отрицательное или равное нулю, в зависимости от выполнения отношения "больше", "меньше" или "равно".

Как правило, в классе вначале определяют метод CompareTo, а после этого вводят перегруженные операции, чтобы выполнять сравнение объектов привычным образом с использованием знаков операций отношения.

Давайте введем отношение порядка на классе Person, рассмотренном в "Классы" , сделав этот класс наследником интерфейса IComparable. Реализуем в этом классе метод интерфейса CompareTo:

public class Person:IComparable
   {
      public int CompareTo( object pers)
      {
         const string s = "Сравниваемый объект не принадлежит классу Person";
         Person p = pers as Person;
         if (!p.Equals(null))
            return (fam.CompareTo(p.fam));
         throw new ArgumentException (s);
      }
      // другие компоненты класса
   }

Поскольку аргумент в методе интерфейса принадлежит типу object, перед выполнением сравнения его нужно привести к типу Person. Для приведения используется операция as, позволяющая проверить корректность выполнения приведения. Если приведение невозможно, то невозможно выполнить и сравнение объектов. В этом случае выбрасывается исключение, которое может обработать разумным образом только клиент класса, пытавшийся выполнить сравнение. В самом классе Person можно только пояснить ситуацию, передав информацию объекту исключения.

Заметьте также, что при проверке на значение null используется отношение Equals, а не обычное равенство, которое будет переопределено.

Приведение типов и операции as и is

Давно пора привести отложенное пояснение операций is и as, полезных при работе с объектами, тип которых может быть не определен. Операция is используется в логических выражениях. Логическое выражение

obj is T

истинно, если объект obj принадлежит типу T, и ложно в противном случае.

Оператор присваивания

obj = P as T;

присваивает объекту obj объект P, приведенный к типу T, если такое приведение возможно, иначе объекту присваивается значение null. Семантику as можно выразить следующим условным выражением:

(P is T) ? (T)P : (T)null
Порядок на классе Person

Определив метод CompareTo для класса Person, мы тем самым ввели отношение порядка для объектов этого класса. Конечно, сравнение персон может выполняться по разным критериям: возрасту, росту, зарплате. В данном случае отношение порядка на объектах класса Person задается как отношение порядка на фамилиях персон. Так как строки наследуют интерфейс IComparable, для фамилий персон вызывается метод CompareTo, его результат и возвращается в качестве результата метода CompareTo для персон.

Введем теперь в нашем классе Person перегрузку операций отношения:

public static bool operator <(Person p1, Person p2)
 {
    return (p1.CompareTo(p2) < 0);
 }
 public static bool operator >(Person p1, Person p2)
 {
    return (p1.CompareTo(p2) > 0);
 }
 public static bool operator <=(Person p1, Person p2)
 {
    return (p1.CompareTo(p2) <= 0);
 }
 public static bool operator >=(Person p1, Person p2)
 {
    return (p1.CompareTo(p2) >=0);
 }
 public static bool operator ==(Person p1, Person p2)
 {
    return (p1.CompareTo(p2) == 0);
 }
 public static bool operator !=(Person p1, Person p2)
 {
    return (p1.CompareTo(p2) != 0);
 }

Как обычно, приведу тестовый пример, проверяющий работу с введенными методами:

public void TestCompare()
 {
    Person poet1 = new Person("Пушкин");
    Person poet2 = new Person("Лермонтов");
    Person poet3 = new Person("Пастернак");
    Person poet4 = new Person("Мандельштам");
    Person poet5 = new Person("Ахматова");
    Person poet6 = new Person("Цветаева");
    Console.WriteLine("{0} > {1} = {2}", poet1.Fam,
       poet2.Fam, (poet1 > poet2));
    Console.WriteLine("{0} >= {1} = {2}", poet3.Fam,
       poet4.Fam, (poet3 >= poet4));
    Console.WriteLine("{0} != {1} = {2}", poet5.Fam,
       poet6.Fam, (poet5 != poet6));
  }

Вот результаты работы этого теста.

Сравнение персон

Рис. 5.6. Сравнение персон

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

< Лекция 5 || Лекция 6: 12345 || Лекция 7 >
Федор Антонов
Федор Антонов

Здравствуйте!

Записался на ваш курс, но не понимаю как произвести оплату.

Надо ли писать заявление и, если да, то куда отправлять?

как я получу диплом о профессиональной переподготовке?

Илья Ардов
Илья Ардов

Добрый день!

Я записан на программу. Куда высылать договор и диплом?

Дмитрий Штаф
Дмитрий Штаф
Россия
Дмитрий Слапогузов
Дмитрий Слапогузов
Россия, Бийск