Condividi tramite


Effettuare il refactoring in funzioni pure (LINQ to XML)

Per le trasformazioni funzionali pure, è fondamentale comprendere come eseguire il refactoring del codice usando funzioni pure.

Nota

La nomenclatura comune nella programmazione funzionale prevede il refactoring dei programmi tramite funzioni pure. In Visual Basic e C++, ciò è in linea con l'uso di funzioni nei rispettivi linguaggi. Tuttavia, in C# le funzioni sono denominate metodi. Ai fini del presente documento, una funzione pura viene implementata come metodo in C#.

Come accennato in precedenza, una funzione pura prevede due caratteristiche utili:

  • Non ha effetti collaterali. La funzione non cambia le variabili o i dati di qualsiasi tipo all'esterno della funzione.
  • È coerente. Dato lo stesso set di dati di input, restituirà sempre lo stesso valore di output.

Per passare alla programmazione funzionale, è possibile eseguire il refactoring del codice esistente per eliminare inutili effetti collaterali e dipendenze esterne. In questo modo, è possibile creare versioni di funzioni pure del codice esistente.

Questo articolo descrive cos'è e cosa non è una funzione pura. L'Esercitazione: Manipolare contenuto in un documento WordprocessingML illustra come manipolare un documento WordprocessingML e include due esempi su come effettuare il refactoring usando una funzione pura.

Negli esempi seguenti vengono confrontate due funzioni non pure e una funzione pura.

Esempio: Implementare una funzione non pura che cambia un membro di classe statico

Nel codice seguente la funzione HyphenatedConcat non è una funzione pura perché modifica la il membro della classe statica 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

Nell'esempio viene prodotto l'output seguente:

StringOne-StringTwo

Si noti che è irrilevante se per i dati da modificare l'accesso è public o private oppure se i dati sono un membro di static, di shared o di istanza. Una funzione pura non cambia i dati all'eterno della funzione.

Esempio: Implementare una funzione non pura che cambia un parametro

Inoltre, la versione seguente di questa stessa funzione non è pura, perché modifica il contenuto del relativo parametro 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

Questa versione del programma produce lo stesso output della prima versione, perché la funzione HyphenatedConcat ha modificato il valore (stato) del primo parametro richiamando la funzione del membro Append. Si noti che questa modifica si verifica nonostante il fatto che HyphenatedConcat usa il passaggio di parametri in base a chiamata per valore.

Importante

Per i tipi di riferimenti, se un parametro viene passato per valore, il risultato è una copia del riferimento a un oggetto passato. Questa copia è ancora associata agli stessi dati di istanza del riferimento originale, finché la variabile di riferimento non viene assegnata a un nuovo oggetto. La chiamata per riferimento non è necessariamente richiesta affinché una funzione modifichi un parametro.

Esempio: Implementare una funzione pura

Nella versione successiva del programma viene illustrato come implementare la funzione HyphenatedConcat come funzione pura.

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

Anche in questo caso, la versione produce la stessa riga di output: StringOne-StringTwo Si noti che per mantenere il valore concatenato, viene archiviato nella variabile intermedia s2.

Un approccio che può risultare utile consiste nello scrivere funzioni localmente non pure, ossia che dichiarano e modificano variabili locali, ma globalmente pure. Tali funzioni presentano molte caratteristiche utili in termini di componibilità, ma evitano alcune delle complessità dei linguaggi di programmazione funzionali, ad esempio la necessità di usare la ricorsione quando con un semplice ciclo si otterrebbe lo stesso risultato.

Operatori di query standard

Una caratteristica importante degli operatori di query standard è che vengono implementati come funzioni pure.

Per altre informazioni, vedere Panoramica degli operatori di query standard (C#) e Panoramica degli operatori di query standard (Visual Basic).

Vedi anche