C# Language
Functionele programmering
Zoeken…
Func en actie
Func biedt een houder voor geparametriseerde anonieme functies. De leidende typen zijn de ingangen en het laatste type is altijd de retourwaarde.
// 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))
Actie objecten zijn als nietig methoden, zodat ze hebben helaas alleen een input type. Er wordt geen resultaat op de evaluatiestapel geplaatst.
// 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.
Onveranderlijkheid
Onveranderlijkheid is gebruikelijk bij functioneel programmeren en zeldzaam bij objectgeoriënteerd programmeren.
Maak bijvoorbeeld een adrestype met veranderlijke status:
public class Address ()
{
public string Line1 { get; set; }
public string Line2 { get; set; }
public string City { get; set; }
}
Elk stukje code kan elke eigenschap in het bovenstaande object wijzigen.
Maak nu het onveranderlijke adrestype:
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;
}
}
Houd er rekening mee dat het hebben van alleen-lezen collecties onveranderlijkheid niet respecteert. Bijvoorbeeld,
public class Classroom
{
public readonly List<Student> Students;
public Classroom(List<Student> students)
{
Students = students;
}
}
is niet onveranderlijk, omdat de gebruiker van het object de verzameling kan wijzigen (elementen toevoegen of verwijderen). Om het onveranderlijk te maken, moet men ofwel een interface als IEnumerable gebruiken, die geen methoden blootstelt om toe te voegen, of het een ReadOnlyCollection maken.
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());
Met het onveranderlijke object hebben we de volgende voordelen:
- Het zal een bekende status hebben (andere code kan het niet veranderen).
- Het is draadveilig.
- De constructor biedt één validatieplaats.
- Als u weet dat het object niet kan worden gewijzigd, wordt de code gemakkelijker te begrijpen.
Vermijd nulverwijzingen
C # -ontwikkelaars krijgen veel nul referentie-uitzonderingen om mee om te gaan. F # -ontwikkelaars doen dat niet omdat ze het Optietype hebben. Een Option <> type (sommigen geven de voorkeur Misschien <> als een naam) biedt een Return en een Return type. Het maakt expliciet dat een methode mogelijk een nulrecord retourneert.
U kunt bijvoorbeeld het volgende niet lezen en weten of u te maken krijgt met een nulwaarde.
var user = _repository.GetUser(id);
Als u weet wat de mogelijke nulwaarde is, kunt u een aantal boilerplate-code invoeren om hiermee om te gaan.
var username = user != null ? user.Name : string.Empty;
Wat als we in plaats daarvan een optie <> hebben geretourneerd?
Option<User> maybeUser = _repository.GetUser(id);
De code maakt nu expliciet dat we misschien een None-record hebben geretourneerd en de boilerplate-code om te controleren op Some of None is vereist:
var username = maybeUser.HasValue ? maybeUser.Value.Name : string.Empty;
De volgende methode laat zien hoe u een optie <> retourneert
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>();
}
Hier is een minimale implementatie van Optie <>.
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);
}
}
Om het bovenstaande te demonstreren, kan createNull.csx worden uitgevoerd met de C # REPL.
Zoals gezegd is dit een minimale implementatie. Een zoekopdracht naar "Misschien" NuGet-pakketten levert een aantal goede bibliotheken op.
Hogere-orde functies
Een hogere orde functie is er een die een andere functie als argument neemt of een functie (of beide) retourneert.
Dit wordt meestal gedaan met lambdas, bijvoorbeeld wanneer een predicaat wordt doorgegeven aan een LINQ Where-clausule:
var results = data.Where(p => p.Items == 0);
De Where () -clausule kan veel verschillende predicaten ontvangen, wat hem een aanzienlijke flexibiliteit biedt.
Het doorgeven van een methode aan een andere methode wordt ook gezien bij het implementeren van het Strategie ontwerppatroon. Afhankelijk van de vereisten tijdens runtime kunnen bijvoorbeeld verschillende sorteermethoden worden gekozen en doorgegeven aan een Sorteermethode voor een object.
Onveranderlijke collecties
Het System.Collections.Immutable
NuGet-pakket biedt onveranderlijke verzamelklassen.
Items maken en toevoegen
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
Maken met behulp van de builder
Bepaalde onveranderlijke collecties hebben een binnenklasse Builder
die kan worden gebruikt om goedkoop grote onveranderlijke instanties te bouwen:
var builder = ImmutableList.CreateBuilder<int>(); // returns ImmutableList.Builder
builder.Add(1);
builder.Add(2);
var list = builder.ToImmutable();
Maken van een bestaande IEnumerable
var numbers = Enumerable.Range(1, 5);
var list = ImmutableList.CreateRange<int>(numbers);
Lijst met alle soorten onveranderlijke collecties:
-
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>