Поделиться через


Рефакторинг в чистые функции (LINQ to XML)

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

Примечание.

Общим подходом к функциональному программированию является оптимизация кода программ для получения чистых функций. В Visual Basic и C++ это равносильно использованию функций. Но в C# функции называются методами. В данном случае чистая функция в C# реализуется как метод.

Как отмечалось ранее в этом разделе, чистые функции имеют две полезные характеристики.

  • У них отсутствуют побочные эффекты. Функция не изменяет переменные или данные любого типа за пределами функции.
  • Это согласованно. После получения того же набора входных данных они всегда возвращают одно и то же выходное значение.

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

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

В следующих примерах сравниваются обычные и чистые функции.

Пример. Реализация не чистой функции, которая изменяет элемент статического класса

В следующем коде HyphenatedConcat функция не является чистой функцией, так как она изменяет aMember элемент статического класса:

public class Program
{
    private static string aMember = "StringOne";

    public static void HyphenatedConcat(string appendStr)
    {
        aMember += '-' + appendStr;
    }

    public static void Main()
    {
        HyphenatedConcat("StringTwo");
        Console.WriteLine(aMember);
    }
}
Module Module1
    Dim aMember As String = "StringOne"

    Public Sub HyphenatedConcat(ByVal appendStr As String)
        aMember = aMember & "-" & appendStr
    End Sub

    Sub Main()
        HyphenatedConcat("StringTwo")
        Console.WriteLine(aMember)
    End Sub
End Module

В примере получается следующий вывод.

StringOne-StringTwo

Обратите внимание, что не имеет значения, имеют public ли измененные данные или private имеют доступ, а также член static , shared член или член экземпляра. Чистая функция не изменяет данные за пределами функции.

Пример. Реализация не чистой функции, которая изменяет параметр

Кроме того, следующая версия этой же функции не является чистой, так как она изменяет содержимое его параметра. sb

public class Program
{
    public static void HyphenatedConcat(StringBuilder sb, String appendStr)
    {
        sb.Append('-' + appendStr);
    }

    public static void Main()
    {
        StringBuilder sb1 = new StringBuilder("StringOne");
        HyphenatedConcat(sb1, "StringTwo");
        Console.WriteLine(sb1);
    }
}
Module Module1
    Public Sub HyphenatedConcat(ByVal sb As StringBuilder, ByVal appendStr As String)
        sb.Append("-" & appendStr)
    End Sub

    Sub Main()
        Dim sb1 As StringBuilder = New StringBuilder("StringOne")
        HyphenatedConcat(sb1, "StringTwo")
        Console.WriteLine(sb1)
    End Sub
End Module

Эта версия программы дает те же выходные данные, что и первая версия, поскольку функция HyphenatedConcat изменила значение (состояние) своего первого параметра, вызвав функцию-член Append. Обратите внимание, что это изменение происходит, несмотря на то HyphenatedConcat , что используется передача параметра по вызову.

Внимание

Передача параметров по значению для ссылочных типов приводит к созданию копии ссылки на передаваемый объект. Эта копия все еще связана с теми же данными экземпляра, что и первоначальная ссылка (пока ссылочная переменная не будет присвоена новому объекту). Для изменения параметра не обязательно требуется ссылка по вызову.

Пример. Реализация чистой функции

Следующая версия этой программы реализует функцию HyphenatedConcat в виде чистой функции.

class Program
{
    public static string HyphenatedConcat(string s, string appendStr)
    {
        return (s + '-' + appendStr);
    }

    public static void Main(string[] args)
    {
        string s1 = "StringOne";
        string s2 = HyphenatedConcat(s1, "StringTwo");
        Console.WriteLine(s2);
    }
}
Module Module1
    Public Function HyphenatedConcat(ByVal s As String, ByVal appendStr As String) As String
        Return (s & "-" & appendStr)
    End Function

    Sub Main()
        Dim s1 As String = "StringOne"
        Dim s2 As String = HyphenatedConcat(s1, "StringTwo")
        Console.WriteLine(s2)
    End Sub
End Module

И снова она дает ту же строку вывода: StringOne-StringTwo. Обратите внимание, что для сохранения сцепленного значения он хранится в промежуточной переменной s2.

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

Стандартные операторы запросов

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

Дополнительные сведения см. в обзоре стандартных операторов запросов (C#) и стандартных операторах запросов (Visual Basic).

См. также