Функции с побочным эффектом

Функция называется функцией с побочным эффектом, если помимо результата, вычисляемого функцией и возвращаемого ей в операторе 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);

}

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

Функции с побочным эффектом - student2.ru

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

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

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

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

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

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

 

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

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

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

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

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

/// <summary>

/// Тестирование перегруженных методов A()

/// </summary>

public void TestLoadMethods()

{

long u=0; double v =0;

A(out u, 7); A(out v, 7.5);

Console.WriteLine ("u= {0}, v= {1}", u,v);

A(out v,7);

Console.WriteLine("v= {0}",v);

A(out u, 7,11,13);

A(out v, 7.5, Math.Sin(11.5)+Math.Cos(13.5), 15.5);

Console.WriteLine (

"u= {0}, v= {1}", u,v);

}//TestLoadMethods

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

Функции с побочным эффектом - student2.ru

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

Наши рекомендации