C# Language
Programmazione funzionale
Ricerca…
Func e azione
Func fornisce un supporto per le funzioni anonime parametrizzate. I tipi principali sono gli input e l'ultimo tipo è sempre il valore restituito.
// square a number.
Func<double, double> square = (x) => { return x * x; };
// get the square root.
// note how the signature matches the built in method.
Func<double, double> squareroot = Math.Sqrt;
// provide your workings.
Func<double, double, string> workings = (x, y) =>
string.Format("The square of {0} is {1}.", x, square(y))
Gli oggetti azione sono come metodi void quindi hanno solo un tipo di input. Nessun risultato è inserito nello stack di valutazione.
// right-angled triangle.
class Triangle
{
public double a;
public double b;
public double h;
}
// Pythagorean theorem.
Action<Triangle> pythagoras = (x) =>
x.h = squareroot(square(x.a) + square(x.b));
Triangle t = new Triangle { a = 3, b = 4 };
pythagoras(t);
Console.WriteLine(t.h); // 5.
Immutabilità
L'immutabilità è comune nella programmazione funzionale e rara nella programmazione orientata agli oggetti.
Creare, ad esempio, un tipo di indirizzo con stato mutabile:
public class Address ()
{
public string Line1 { get; set; }
public string Line2 { get; set; }
public string City { get; set; }
}
Qualsiasi parte di codice potrebbe alterare qualsiasi proprietà nell'oggetto sopra.
Ora crea il tipo di indirizzo immutabile:
public class Address ()
{
public readonly string Line1;
public readonly string Line2;
public readonly string City;
public Address(string line1, string line2, string city)
{
Line1 = line1;
Line2 = line2;
City = city;
}
}
Ricordare che disporre di raccolte di sola lettura non rispetta l'immutabilità. Per esempio,
public class Classroom
{
public readonly List<Student> Students;
public Classroom(List<Student> students)
{
Students = students;
}
}
non è immutabile, in quanto l'utente dell'oggetto può modificare la raccolta (aggiungere o rimuovere elementi da essa). Per renderlo immutabile, si deve usare un'interfaccia come IEnumerable, che non espone i metodi da aggiungere, o per renderlo un ReadOnlyCollection.
public class Classroom
{
public readonly ReadOnlyCollection<Student> Students;
public Classroom(ReadOnlyCollection<Student> students)
{
Students = students;
}
}
List<Students> list = new List<Student>();
// add students
Classroom c = new Classroom(list.AsReadOnly());
Con l'oggetto immutabile abbiamo i seguenti vantaggi:
- Sarà in uno stato noto (altro codice non può cambiarlo).
- È thread-safe.
- Il costruttore offre un singolo posto per la convalida.
- Sapere che l'oggetto non può essere modificato rende il codice più facile da capire.
Evita riferimenti null
Gli sviluppatori C # ottengono un sacco di eccezioni di riferimento null da gestire. Gli sviluppatori di F # non lo fanno perché hanno il tipo Option. Un tipo di opzione <> (alcuni preferiscono forse <> come nome) fornisce un tipo di ritorno Some e None. Rende esplicito che un metodo potrebbe essere in procinto di restituire un record nullo.
Ad esempio, non puoi leggere quanto segue e sapere se dovrai gestire un valore nullo.
var user = _repository.GetUser(id);
Se si conosce l'eventuale null, è possibile introdurre un codice di codice per affrontarlo.
var username = user != null ? user.Name : string.Empty;
Cosa succede se invece è stata restituita un'opzione <>?
Option<User> maybeUser = _repository.GetUser(id);
Il codice ora rende esplicito che potremmo avere un record None restituito e il codice boilerplate da verificare per Some o None è richiesto:
var username = maybeUser.HasValue ? maybeUser.Value.Name : string.Empty;
Il seguente metodo mostra come restituire un'opzione <>
public Option<User> GetUser(int id)
{
var users = new List<User>
{
new User { Id = 1, Name = "Joe Bloggs" },
new User { Id = 2, Name = "John Smith" }
};
var user = users.FirstOrDefault(user => user.Id == id);
return user != null ? new Option<User>(user) : new Option<User>();
}
Ecco un'implementazione minima dell'opzione <>.
public struct Option<T>
{
private readonly T _value;
public T Value
{
get
{
if (!HasValue)
throw new InvalidOperationException();
return _value;
}
}
public bool HasValue
{
get { return _value != null; }
}
public Option(T value)
{
_value = value;
}
public static implicit operator Option<T>(T value)
{
return new Option<T>(value);
}
}
Per dimostrare quanto sopra avoidNull.csx può essere eseguito con C # REPL.
Come affermato, questa è un'implementazione minima. Una ricerca di pacchetti "Forse" NuGet mostrerà un buon numero di buone librerie.
Funzioni di ordine superiore
Una funzione di ordine superiore è quella che accetta un'altra funzione come argomento o restituisce una funzione (o entrambe).
Questo è comunemente fatto con lambdas, ad esempio quando si passa un predicato a una clausola LINQ Where:
var results = data.Where(p => p.Items == 0);
La clausola Where () potrebbe ricevere molti predicati diversi che gli conferiscono una notevole flessibilità.
Passare un metodo in un altro metodo è anche visto quando si implementa il modello di progettazione della strategia. Ad esempio, vari metodi di ordinamento potrebbero essere scelti e passati in un metodo di ordinamento su un oggetto in base ai requisiti in fase di esecuzione.
Collezioni immutabili
Il pacchetto System.Collections.Immutable
NuGet offre classi di raccolta immutabili.
Creazione e aggiunta di elementi
var stack = ImmutableStack.Create<int>();
var stack2 = stack.Push(1); // stack is still empty, stack2 contains 1
var stack3 = stack.Push(2); // stack2 still contains only one, stack3 has 2, 1
Creare usando il costruttore
Alcune collezioni immutabili dispongono di una classe interna Builder
che può essere utilizzata per creare a buon mercato istanze immutabili di grandi dimensioni:
var builder = ImmutableList.CreateBuilder<int>(); // returns ImmutableList.Builder
builder.Add(1);
builder.Add(2);
var list = builder.ToImmutable();
Creazione da un oggetto I esistente
var numbers = Enumerable.Range(1, 5);
var list = ImmutableList.CreateRange<int>(numbers);
Elenco di tutti i tipi di raccolta immutabili:
-
System.Collections.Immutable.ImmutableArray<T>
-
System.Collections.Immutable.ImmutableDictionary<TKey,TValue>
-
System.Collections.Immutable.ImmutableHashSet<T>
-
System.Collections.Immutable.ImmutableList<T>
-
System.Collections.Immutable.ImmutableQueue<T>
-
System.Collections.Immutable.ImmutableSortedDictionary<TKey,TValue>
-
System.Collections.Immutable.ImmutableSortedSet<T>
-
System.Collections.Immutable.ImmutableStack<T>