Опубликован: 15.09.2010 | Доступ: свободный | Студентов: 4807 / 630 | Оценка: 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. Перегрузка операций отношения

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

Вася меньше Пети
Stefan Berzan
Stefan Berzan
Молдова, Кишинев
Дмитрий Казимиров
Дмитрий Казимиров
Россия, Омск