Тверской государственный университет
Опубликован: 02.12.2009 | Доступ: свободный | Студентов: 3452 / 683 | Оценка: 4.41 / 4.23 | Длительность: 09:18:00
ISBN: 978-5-9963-0259-8
Лекция 5:

Процедуры и функции - методы класса

< Лекция 4 || Лекция 5: 12345 || Лекция 6 >
Функции с побочным эффектом

Функция называется функцией с побочным эффектом, если помимо результата, вычисляемого функцией и возвращаемого ей в операторе return, она имеет выходные аргументы с ключевыми словами ref и out. В языках C/C++ функции с побочным эффектом применяются сплошь и рядом. Хороший стиль ОО-программирования не рекомендует применение таких функций. Выражения, использующие функции с побочным эффектом, могут потерять свои прекрасные свойства, присущие им в математике. Если F(a) - функция с побочным эффектом, то a + F(a) может быть не равно F(a) + a, так что теряется коммутативность операции сложения.

Примером такой функции является функция F, приведенная выше. Вот тест, демонстрирующий потерю коммутативности сложения при работе с этой функцией:

/// <summary>
    /// тестирование побочного эффекта
    /// </summary>
    public void TestSideEffect()
    {
      int a = 0, b=0, c=0;
      a = 1; b = a + F(ref a);
      a = 1; c = F(ref a) + a;
      Console.WriteLine("a={0}, b={1}, c={2}",a, b, c);
    }

На рис. 5.2 показаны результаты работы этого метода.

Демонстрация вызова функции с побочным эффектом

Рис. 5.2. Демонстрация вызова функции с побочным эффектом

Обратите внимание на полезность указания ключевого слова ref в момент вызова. Его появление хоть как-то оправдывает некоммутативность сложения.

Напомню, что и выражения с побочным эффектом также приводят к потере коммутативности сложения и умножения. Выражение x + ++x не эквивалентно выражению ++x + x.

Методы. Перегрузка

Должно ли быть уникальным имя метода в классе? Нет, это не требуется. Более того, проектирование методов с одним и тем же именем является частью стиля программирования на С++ и стиля C#. Существование в классе методов с одним и тем же именем называется перегрузкой, а сами одноименные методы называются перегруженными.

Перегрузка методов полезна, когда требуется решать подобные задачи с разным набором аргументов. Типичный пример - это нахождение площади треугольника. Площадь можно вычислить по трем сторонам, по двум углам и стороне, по двум сторонам и углу между ними и при многих других наборах аргументов. Считается удобным во всех случаях иметь для метода одно имя, например, Square, и всегда, когда нужно вычислить площадь, не задумываясь вызывать метод Square, передавая ему известные в данный момент аргументы.

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

Перегрузка характерна и для знаков операций. В зависимости от типов аргументов один и тот же знак может выполнять фактически разные операции. Классическим примером является знак операции сложения +, который играет роль операции сложения не только для арифметических данных разных типов, но и выполняет конкатенацию строк.

Перегрузка требует уточнения семантики вызова метода. Когда встречается вызов не перегруженного метода, то имя метода в вызове однозначно определяет, тело какого метода должно выполняться в точке вызова. Когда же метод перегружен, то знания имени недостаточно - оно не уникально. Уникальной характеристикой перегруженных методов является их сигнатура. Перегруженные методы, имея одинаковое имя, должны отличаться либо числом аргументов, либо их типами, либо ключевыми словами (заметьте, с точки зрения сигнатуры ключевые слова ref и out не отличаются). Уникальность сигнатуры позволяет вызвать требуемый перегруженный метод.

Выше уже были приведены четыре перегруженных метода с именем Cube, отличающиеся сигнатурой. Методы отличаются типами аргументов и ключевым словом params. Когда вызывается метод Cube с двумя аргументами, в зависимости от типа будет вызываться реализация, не содержащая аргумент с модификатором params. Когда же число аргументов больше двух, работает реализация, позволяющая справиться с заранее не фиксированным числом аргументов. Заметьте, эта реализация может прекрасно работать и для случая двух аргументов, но полезно иметь частные случаи для фиксированного набора аргументов. При поиске подходящего перегруженного метода частные случаи получают предпочтение в сравнении с общим случаем.

Насколько полезна перегрузка методов? Здесь нет экономии кода, поскольку каждую реализацию нужно задавать явно; нет выигрыша по времени, скорее требуются определенные затраты на поиск подходящей реализации, который может приводить к конфликтам, к счастью, обнаруживаемым на этапе компиляции. В нашем примере вполне разумно было бы отказаться от перегрузки и иметь четыре метода с разными именами, осознанно вызывая метод, применимый к конкретным данным.

Есть ситуации, где перегрузка полезна, недаром она широко используется при построении библиотеки FCL. Возьмем, например, класс Convert, у которого 16 методов с разными именами, зависящими от целевого типа преобразования. Каждый из этих 16 методов перегружен и, в свою очередь, имеет примерно 16 реализаций в зависимости от типа источника. Согласитесь, что неразумно было бы иметь в классе Convert 256 методов вместо 16 перегруженных методов. Впрочем, так же неразумно было бы иметь один перегруженный метод, имеющий 256 реализаций. Перегрузка - это инструмент, которым следует пользоваться с осторожностью и обоснованно.

В заключение этой темы посмотрим, как проводилось тестирование работы с перегруженными методами:

/// <summary>
  /// Тестирование перегруженных методов Cube()
  /// </summary>
  public void TestLoadMethods()
  {
      long u = 0; double v = 0;
      Cube(out u, 7); Cube(out v, 7.5);
      Console.WriteLine("u= {0}, v= {1}", u, v);
      Cube(out v, 7);
      Console.WriteLine("v = {0}", v);
      Cube(out u, 7, 11, 13);
      Cube(out v, 7.5, Math.Sin(11.5) + Math.Cos(13.5), 15.5);
      Console.WriteLine("u= {0}, v= {1}", u, v);
  }//TestLoadMethods

На рис. 5.3 показаны результаты этого тестирования.

Тестирование перегрузки методов

Рис. 5.3. Тестирование перегрузки методов

Архитектура проекта

Как обычно, для поддержки примеров этой главы создано Решение с именем Ch5, содержащее консольный проект ProcAndFun. Помимо автоматически созданного класса Program, в проект добавлены три класса - Testing, Account, Account1. В Main процедуре класса Program создается объект testObject класса Testing, вызывающий методы этого класса. Каждый из методов представляет собой тест, позволяющий на примере пояснить излагаемый материал.

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

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

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

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

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

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

Добрый день!

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