# Funzione e azione
Funzione fornisce un titolare per funzioni anonime parametrizzate. I tipi iniziali 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))
Azione gli oggetti sono come metodi void, quindi hanno solo un tipo di input. Nessun risultato viene 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.
# Evita riferimenti nulli
Gli sviluppatori C# ottengono molte eccezioni di riferimento null da gestire. Gli sviluppatori F# non lo fanno perché hanno il tipo Option. Un tipo Option<> (alcuni preferiscono Maybe<> come nome) fornisce un tipo restituito 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 conosci il possibile null puoi introdurre del codice standard per gestirlo.
var username = user != null ? user.Name : string.Empty;
E se invece avessimo un'Opzione<> restituita?
Option<User> maybeUser = _repository.GetUser(id);
Il codice ora rende esplicito che potremmo avere un record Nessuno restituito ed è richiesto il codice standard per verificare la presenza di Alcuni o Nessuno:
var username = maybeUser.HasValue ? maybeUser.Value.Name : string.Empty;
Il metodo seguente 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 di Option<>.
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 detto, si tratta di un'implementazione minima. Una ricerca di pacchetti NuGet "Forse" mostrerà una serie 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 viene comunemente fatto con lambda, 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, il che le conferisce una notevole flessibilità.
Il passaggio di un metodo a un altro metodo viene visualizzato anche quando si implementa il modello di progettazione della strategia. Ad esempio, è possibile scegliere e passare vari metodi di ordinamento in un metodo Sort su un oggetto a seconda dei requisiti in fase di esecuzione.
# Immutabilità
L'immutabilità è comune nella programmazione funzionale e rara nella programmazione orientata agli oggetti.
Crea, 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 pezzo 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;
}
}
Tieni presente che avere raccolte di sola lettura non rispetta l'immutabilità. Ad 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ò alterare la raccolta (aggiungere o rimuovere elementi da essa). Per renderlo immutabile, è necessario utilizzare un'interfaccia come IEnumerable, che non espone metodi da aggiungere, o renderlo una 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 (l'altro codice non può cambiarlo).
- È thread-safe.
- Il costruttore offre un unico posto per la convalida.
- Sapere che l'oggetto non può essere alterato rende il codice più facile da capire.
# raccolte immutabili
Il System.Collections.Immutable
Il pacchetto NuGet fornisce classi di raccolta non modificabili.
# 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
# Creazione utilizzando il builder
Alcune raccolte immutabili hanno un Builder
classe interna che può essere utilizzata per costruire 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 IEnumerable 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>