Опубликован: 15.09.2010 | Доступ: свободный | Студентов: 5578 / 969 | Оценка: 3.97 / 3.80 | Длительность: 14:45:00
Лекция 9:

Интерфейсы. Контейнерные классы

Сортировка по разным критериям (интерфейс IComparer)

Интерфейс IComparer определен в пространстве имен System.Collections. Он содержит один метод Compare, возвращающий результат сравнения двух объектов, переданных ему в качестве параметров:

interface IComparer
{
    int Compare( object ob1, object ob2 )
}

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

Пример сортировки массива объектов из предыдущего листинга по именам (свойство Name, класс SortByName ) и количеству вооружений (свойство Ammo, класс SortByAmmo ) приведен в листинге 9.2.

using System;
using System.Collections;
namespace ConsoleApplication1
{
    class Monster
    {
        public Monster( int health, int ammo, string name )
        {
            this.health = health;
            this.ammo   = ammo;
            this.name   = name;
        }
        
        public int Ammo 
        {
            get { return ammo; }
            set 
            {
                if (value > 0) ammo = value;
                else           ammo = 0;
            }
        }

        public string Name 
        {
            get { return name; }
        }

        virtual public void Passport()
        {
            Console.WriteLine( "Monster {0} \t health = {1} ammo = {2}", 
                               name, health, ammo );
        }

        public class SortByName : IComparer                               //
        {
            int IComparer.Compare( object ob1, object ob2 )
            {
                Monster m1 = (Monster) ob1;
                Monster m2 = (Monster) ob2;
                return String.Compare( m1.Name, m2.Name );
            }
        }

        public class SortByAmmo : IComparer                               //
        {
            int IComparer.Compare( object ob1, object ob2 )
            {
                Monster m1 = (Monster) ob1;
                Monster m2 = (Monster) ob2;
                if ( m1.Ammo > m2.Ammo ) return  1;
                if ( m1.Ammo < m2.Ammo ) return -1;
                return 0;
            }
        }
        string name;
        int health, ammo;
    }
    
    class Class1
    {   static void Main()
        {
            const int n = 3;
            Monster[] stado = new Monster[n];

            stado[0] = new Monster( 50, 50, "Вася" );
            stado[1] = new Monster( 80, 80, "Петя" );
            stado[2] = new Monster( 40, 10, "Маша" );

            Console.WriteLine( "Сортировка по имени:" );
            Array.Sort( stado, new Monster.SortByName() );
            foreach ( Monster elem in stado ) elem.Passport();

            Console.WriteLine( "Сортировка по вооружению:" );
            Array.Sort( stado, new Monster.SortByAmmo() );
            foreach ( Monster elem in stado ) elem.Passport();
        }
    }
}
Листинг 9.2. Сортировка по двум критериям

Результат работы программы:

Сортировка по имени:
Monster Вася     health = 50 ammo = 50
Monster Маша     health = 40 ammo = 10
Monster Петя     health = 80 ammo = 80
Сортировка по вооружению:
Monster Маша     health = 40 ammo = 10
Monster Вася     health = 50 ammo = 50
Monster Петя     health = 80 ammo = 80
Перегрузка операций отношения

Если класс реализует интерфейс IComparable, его экземпляры можно сравнивать между собой на больше-меньше. Логично разрешить использовать для этого операции отношения, перегрузив их. Операции должны перегружаться парами: < и >, <= и >=, == и !=. Перегрузка операций обычно выполняется путем делегирования, то есть обращения к переопределенным методам CompareTo и Equals.

Примечание

Если класс реализует интерфейс IComparable, требуется переопределить метод Equals и связанный с ним метод GetHashCode. Оба метода унаследованы от базового класса object.

В листинге 9.3 операции отношения перегружены для класса Monster. В качестве критерия сравнения объектов на больше-меньше выступает поле health, а при сравнении на равенство попарно сравниваются все поля объектов

using System;
namespace ConsoleApplication1
{
    class Monster : IComparable
    {
        public Monster( int health, int ammo, string name )
        {
            this.health = health;
            this.ammo   = ammo;
            this.name   = name;
        }

        public override bool Equals( object obj )
        {
            if ( obj == null || GetType() != obj.GetType() ) return false;

            Monster temp = (Monster) obj;
            return health == temp.health &&
                   ammo   == temp.ammo   &&
                   name   == temp.name;
        }

        public override int GetHashCode()
        {
            return name.GetHashCode();
        }

        public static bool operator == ( Monster a, Monster b )
        {
            return a.Equals( b );
        }

        public static bool operator != ( Monster a, Monster b )
        {
            return ! a.Equals( b );
        }

        public static bool operator < ( Monster a, Monster b )
        {
            return ( a.CompareTo( b ) < 0 );
        }
        
        public static bool operator > ( Monster a, Monster b )
        {
            return ( a.CompareTo( b ) > 0 );
        }
        
        public static bool operator <= ( Monster a, Monster b )
        {
            return ( a.CompareTo( b ) <= 0 );
        }
        
        public static bool operator >= ( Monster a, Monster b )
        {
            return ( a.CompareTo( b ) >= 0 );
        }

        public int CompareTo( object obj )
        {
            Monster temp = (Monster) obj;
            if ( this.health > temp.health ) return  1;
            if ( this.health < temp.health ) return -1;
            return 0;
        }

        string name;
        int health, ammo;
    }

    class Class1
    {   static void Main()
        {
            Monster Вася = new Monster( 70, 80, "Вася" );
            Monster Петя = new Monster( 80, 80, "Петя" );
        
            if      ( Вася > Петя )  Console.WriteLine( "Вася больше Пети");
            else if ( Вася == Петя ) Console.WriteLine( "Вася == Петя");
            else                     Console.WriteLine( "Вася меньше Пети");
        }
    }
}
Листинг 9.3. Перегрузка операций отношения

Результат работы программы:

Вася меньше Пети
Георгий Кузнецов
Георгий Кузнецов

"Сокрытие деталей реализации называется инкапсуляцией (от слова "капсула"). "

Сколько можно объяснять?!

ИНКАПСУЛЯЦИЯ НЕ РАВНА СОКРЫТИЮ!!!

Инкапсуляция это парадигма ООП, которая ОБЕСПЕЧИВАЕТ СОКРЫТИЕ!!!

НО СОКРЫТИЕМ  НЕ ЯВЛЯЕТСЯ!!! 

Если буровая коронка обеспечивает разрушение породы, то является ли она сама разрушением породы? Конечно нет!

Ольга Притоманова
Ольга Притоманова