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

Структуры

< Лекция 2 || Лекция 3: 1234 || Лекция 4 >

Класс Rational или структура Rational

Вернемся к классу Rational, спроектированному в предыдущей лекции. Очевидно, что его вполне разумно представить в виде структуры. Наследование для него необязательно. Семантика присваивания развернутого типа больше подходит для рациональных чисел, чем ссылочная семантика, ведь рациональные числа - это еще один подкласс арифметического класса. В общем, класс Rational - прямой кандидат в структуры. Зададимся вопросом, насколько просто объявление класса превратить в объявление структуры? Достаточно ли заменить слово class словом struct? В данном случае заменой одного слова не обойтись. Нужно справиться и с другими вышеперечисленными ограничениями на структуры. Сделать это не трудно. И я приведу объявление структуры SRational, оставляя читателю возможность самому найти все различия между описаниями класса Rational и описанием структуры. Замечу, что, следуя правилам хорошего стиля, требующего давать полям содержательные имена, изменены имена полей. Для русскоязычных читателей английские названия, возможно, менее приятны, чем аббревиатуры m и n.

/// <summary>
/// Структура SRational.
/// определяет новый тип данных - рациональные числа и основные
/// операции над ними - сложение, умножение, вычитание и деление.
/// Рациональное число задается парой целых чисел
/// (numerator,denominator) и изображается
/// обычно в виде дроби numerator/denominator. 
/// Число numerator называется числителем,
/// denominator - знаменателем. Для рационального числа существует
/// множество его представлений, например, 1/2, 2/4, 3/6, 6/12.
/// Среди всех представлений
/// можно выделить то, в котором числитель и знаменатель взаимно
/// несократимы. Такой представитель будет храниться в полях класса.
/// </summary>
public struct SRational
{
    const string NONE_EXIST =
            "Не существует рационального  числа " +
            "со знаменателем, равным нулю!";
    //Поля класса. Числитель и знаменатель рационального числа.
    int numerator, denominator;       
    /// <summary>
    /// Конструктор класса. Создает рациональное число
    /// эквивалентное numerator/denominator, 
    /// но со взаимно несократимыми числителем и знаменателем. 
    /// Если denominator = 0, то выбрасывается исключение, 
    /// сообщающее о невозможности создать
    /// рациональное число со знаменателем 0
    /// </summary>
    /// <param name="numerator">числитель</param>
    /// <param name="denominator">знаменатель</param>
    public SRational(int numerator, int denominator)
    {
        if (denominator == 0) 
            throw new SRationalException(NONE_EXIST);
        if (numerator == 0) 
        { 
            this.numerator = 0; 
            this.denominator = 1; 
            return; 
        }             
        //приведение знака
        if (denominator < 0) 
        { 
            denominator = -denominator; 
            numerator = -numerator; 
        }
        //приведение к несократимой дроби
        int m = numerator, n = denominator;
        {
            int p = 0;
            m = Math.Abs(m); n = Math.Abs(n);
            do
            {
                p = m % n; m = n; n = p;
            } while (n != 0);
        }//Nod
        this.numerator = numerator / m; 
        this.denominator = denominator / m;            
    }       

    /// <summary>
    /// Представление рационального числа 
    /// в виде строки
    /// </summary>
    /// <returns>строка в формате numerator/denominator
    /// </returns>
    public override string ToString()
    {
        return numerator + "/" + denominator;
    }
    public SRational Plus(SRational a)
    {
        int u, v;
        u = numerator * a.denominator + denominator * a.numerator;
        v = denominator * a.denominator;
        return (new SRational(u, v));
    }//Plus
    public static SRational operator +(SRational r1, SRational r2)
    {
        return (r1.Plus(r2));
    }
    public SRational Minus(SRational a)
    {
        int u, v;
        u = numerator * a.denominator - denominator * a.numerator; 
        v = denominator * a.denominator;
        return (new SRational(u, v));
    }//Minus
    public static SRational operator -(SRational r1, SRational r2)
    {
        return (r1.Minus(r2));
    }
    public SRational Mult(SRational a)
    {
        int u, v;
        u = numerator * a.numerator; 
        v = denominator * a.denominator;
        return (new SRational(u, v));
    }//Mult
    public static SRational operator *(SRational r1, SRational r2)
    {
        return (r1.Mult(r2));
    }
    public SRational Divide(SRational a)
    {
        int u, v;
        u = numerator * a.denominator; 
        v = denominator * a.numerator;
        return (new SRational(u, v));
    }//Divide
    public static SRational operator /(SRational r1, SRational r2)
    {
        return (r1.Divide(r2));
    }
    //Константы класса 0 и 1 - ZERO и ONE
    public static readonly SRational ZERO, ONE;
    SRational(int num, int den, string t)
    {
        numerator = num; denominator = den;
    }//Закрытый конструктор
    static SRational()
    {
        ZERO = new SRational(0, 1, "");
        ONE = new SRational(1, 1, "");
    }//Статический конструктор
    //Операции отношения
    public static bool operator ==(SRational r1, SRational r2)
    {
        return ((r1.numerator == r2.numerator) && 
            (r1.denominator == r2.denominator));
    }
    public static bool operator !=(SRational r1, SRational r2)
    {
        return ((r1.numerator != r2.numerator) || 
            (r1.denominator != r2.denominator));
    }        
    public static bool operator <(SRational r1, SRational r2)
    {
        return (r1.numerator * r2.denominator < 
            r2.numerator * r1.denominator);
    }
    public static bool operator >(SRational r1, SRational r2)
    {
        return (r1.numerator * r2.denominator > 
            r2.numerator * r1.denominator);
    }
    public static bool operator <(SRational r1, double r2)
    {
        return ((double)r1.numerator / r1.denominator < r2);
    }
    public static bool operator >(SRational r1, double r2)
    {
        return ((double)r1.numerator / r1.denominator > r2);
    }
    public override bool Equals(object obj)
    {
        return this == (SRational)obj;
    }
    public override int GetHashCode()
    {
        return numerator + denominator;
    }
}
/// <summary>
/// Класс, задающий исключения при работе
/// с рациональными числами.
/// </summary>
public class SRationalException : Exception
{
    public SRationalException() { }
    public SRationalException(string message) :
        base(message) { }
    public SRationalException(string message,
        Exception e)
        : base(message, e) { }
}

Все ранее построенные примеры работы с классом Rational применимы и при работе со структурой SRational и будут давать эквивалентные результаты за одним исключением. При вызове конструктора без аргументов для класса Rational создается корректное рациональное число 0/1. Для структуры конструктор по умолчанию создает некорректное число 0/0. Эту ситуацию никак нельзя исправить, поскольку этот конструктор нельзя переопределить. Единственный выход - не пользоваться этим конструктором при работе со структурами.

Построим пример работы с объектами структуры SRational:

public void TwoSemantics()
   {
       SRational sr1 = new SRational(1, 3), 
          sr2 = new SRational(3, 5);
       SRational sr3, sr4;
       sr3 = sr1 + sr2; sr4 = sr3;
       if (sr3 > 1) sr3 = sr1 + sr3 + SRational.ONE; 
       else sr3 = sr2 + sr3 - SRational.ONE;
       Console.WriteLine("Структура SRational");
       Console.WriteLine("sr1 = " + sr1.ToString());
       Console.WriteLine("sr2 = " + sr2.ToString());
       Console.WriteLine("sr3 = " + sr3.ToString());
       Console.WriteLine("sr4 = " + sr4.ToString());
   }

В этом примере используются константы, работает статический конструктор, закрытый конструктор, перегруженные операции сравнения, арифметические выражения над рациональными числами. В результате вычислений r3 получит значение 8/15, r4 - 14/15. Заметьте, аналогичный пример для класса Rational даст те же результаты. Для класса Rational и структуры Rational нельзя обнаружить разницу между ссылочным и развернутым присваиванием. Это связано с особенностью класса Rational - он по построению относится к неизменяемым ( immutable ) классам, аналогично классу string. Операции этого класса не изменяют поля объекта, а каждый раз создают новый объект. В этом случае можно считать, что объекты класса обладают присваиванием развернутого типа.

< Лекция 2 || Лекция 3: 1234 || Лекция 4 >
Федор Антонов
Федор Антонов

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

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

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

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

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

Добрый день!

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

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