Udostępnij za pośrednictwem


Refaktoryzacja do czystych funkcji (LINQ to XML)

Ważnym aspektem czystych przekształceń funkcjonalnych jest uczenie się, jak refaktoryzować kod przy użyciu czystych funkcji.

Uwaga

Powszechną nomenklaturą w programowaniu funkcjonalnym jest refaktoryzacja programów przy użyciu czystych funkcji. W języku Visual Basic i C++jest to zgodne z użyciem funkcji w odpowiednich językach. Jednak w języku C#funkcje są nazywane metodami. Na potrzeby tej dyskusji czysta funkcja jest implementowana jako metoda w języku C#.

Jak wspomniano wcześniej w tej sekcji, czysta funkcja ma dwie przydatne cechy:

  • Nie ma skutków ubocznych. Funkcja nie zmienia żadnych zmiennych ani danych dowolnego typu poza funkcją.
  • Jest spójny. Biorąc pod uwagę ten sam zestaw danych wejściowych, zawsze zwraca tę samą wartość wyjściową.

Jednym ze sposobów przejścia do programowania funkcjonalnego jest refaktoryzacja istniejącego kodu w celu wyeliminowania niepotrzebnych skutków ubocznych i zależności zewnętrznych. W ten sposób można tworzyć czyste wersje funkcji istniejącego kodu.

W tym artykule omówiono, czym jest czysta funkcja i czym nie jest. Samouczek: manipulowanie zawartością w dokumencie WordprocessingML pokazuje, jak manipulować dokumentem WordprocessingML i zawiera dwa przykłady refaktoryzacji przy użyciu czystej funkcji.

W poniższych przykładach porównane są dwie nieczyste funkcje i czysta funkcja.

Przykład: Implementowanie funkcji nieczystej, która zmienia składową klasy statycznej

W poniższym kodzie funkcja nie jest czystą funkcją, HyphenatedConcat ponieważ modyfikuje statyczną aMember składową klasy:

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

Ten przykład generuje następujące wyniki:

StringOne-StringTwo

Należy pamiętać, że nie ma znaczenia, czy modyfikowane dane mają public dostęp private , czy też są static członkami, shared członkiem lub członkiem wystąpienia. Czysta funkcja nie zmienia żadnych danych poza funkcją.

Przykład: Implementowanie funkcji nieczystej, która zmienia parametr

Ponadto następująca wersja tej samej funkcji nie jest czysta, ponieważ modyfikuje zawartość parametru 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

Ta wersja programu generuje te same dane wyjściowe co pierwsza wersja, ponieważ HyphenatedConcat funkcja zmieniła wartość (stan) pierwszego parametru, wywołując funkcję składową Append . Należy pamiętać, że ta zmiana występuje pomimo faktu, że HyphenatedConcat używa przekazywania parametru call-by-value.

Ważne

W przypadku typów odwołań, jeśli przekażesz parametr według wartości, spowoduje to przekazanie kopii odwołania do przekazanego obiektu. Ta kopia jest nadal skojarzona z tymi samymi danymi wystąpienia co oryginalne odwołanie (dopóki zmienna referencyjna nie zostanie przypisana do nowego obiektu). Wywołanie po odwołaniu nie musi być wymagane do zmodyfikowania parametru przez funkcję.

Przykład: Implementowanie czystej funkcji

W następnej wersji programu pokazano, jak zaimplementować HyphenatedConcat funkcję jako czystą funkcję.

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

Ponownie ta wersja generuje ten sam wiersz danych wyjściowych: StringOne-StringTwo. Należy pamiętać, że aby zachować połączoną wartość, jest ona przechowywana w zmiennej s2pośredniej .

Jednym z podejść, które może być bardzo przydatne, jest pisanie funkcji, które są lokalnie nieczyste (czyli deklarują i modyfikują zmienne lokalne), ale są globalnie czyste. Takie funkcje mają wiele pożądanych cech komponowania, ale unikaj niektórych bardziej zawiłych idiomów programowania funkcjonalnego, takich jak konieczność używania rekursji, gdy prosta pętla zrobiłaby to samo.

Standardowe operatory zapytań

Ważną cechą standardowych operatorów zapytań jest to, że są one implementowane jako czyste funkcje.

Aby uzyskać więcej informacji, zobacz Omówienie standardowych operatorów zapytań (C#) i Standardowe operatory zapytań — omówienie (Visual Basic).

Zobacz też