Suche…


Funktion und Aktion

Func bietet eine Halterung für parametrisierte anonyme Funktionen. Die führenden Typen sind die Eingaben und der letzte Typ ist immer der Rückgabewert.

// 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))

Aktionsobjekte sind wie void-Methoden, daher haben sie nur einen Eingabetyp. Es wird kein Ergebnis auf dem Auswertungsstapel abgelegt.

// 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.

Unveränderlichkeit

Unveränderlichkeit ist bei der funktionalen Programmierung üblich und bei der objektorientierten Programmierung selten.

Legen Sie beispielsweise einen Adresstyp mit veränderbarem Status an:

public class Address () 
{
    public string Line1 { get; set; }
    public string Line2 { get; set; }
    public string City  { get; set; }
}

Jeder Code kann jede Eigenschaft im obigen Objekt ändern.

Erstellen Sie nun den unveränderlichen Adresstyp:

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;
    }
}

Denken Sie daran, dass schreibgeschützte Sammlungen die Unveränderlichkeit nicht respektieren. Zum Beispiel,

public class Classroom
{
    public readonly List<Student> Students;
    
    public Classroom(List<Student> students)
    {
        Students = students;
    }
}

ist nicht unveränderlich, da der Benutzer des Objekts die Sammlung ändern kann (Elemente hinzufügen oder daraus entfernen). Um es unveränderlich zu machen, muss man entweder eine Schnittstelle wie IEnumerable verwenden, die keine hinzuzufügenden Methoden verfügbar macht, oder es zu einer ReadOnlyCollection machen.

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());   

Mit dem unveränderlichen Objekt haben wir folgende Vorteile:

  • Es befindet sich in einem bekannten Zustand (anderer Code kann es nicht ändern).
  • Es ist fadensicher.
  • Der Konstruktor bietet eine einzige Stelle für die Validierung.
  • Wenn Sie wissen, dass das Objekt nicht geändert werden kann, ist der Code verständlicher.

Vermeiden Sie Nullreferenzen

C # -Entwickler erhalten eine Menge von Nullreferenzausnahmen. F # -Entwickler tun dies nicht, weil sie den Option-Typ haben. Ein Option <> -Typ (einige bevorzugen vielleicht <> als Namen) enthalten den Rückgabetyp Some und None. Es macht explizit deutlich, dass eine Methode im Begriff ist, einen Null-Datensatz zurückzugeben.

Beispielsweise können Sie Folgendes nicht lesen und wissen, ob Sie mit einem Nullwert arbeiten müssen.

var user = _repository.GetUser(id);

Wenn Sie die mögliche Null kennen, können Sie einen Boilerplate-Code einführen, um damit umzugehen.

var username = user != null ? user.Name : string.Empty;

Was ist, wenn wir stattdessen eine Option <> zurückgegeben haben?

Option<User> maybeUser = _repository.GetUser(id);

Der Code macht jetzt explizit deutlich, dass möglicherweise ein None-Datensatz zurückgegeben wird, und der Code für die Plattenspeicherung, der auf Some oder None überprüft werden muss:

var username = maybeUser.HasValue ? maybeUser.Value.Name : string.Empty;

Die folgende Methode zeigt, wie eine Option <> zurückgegeben wird

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 ist eine minimale Implementierung von 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);
    }
}

Zur Veranschaulichung der obigen Vermeidung kann Null.csx mit C # REPL ausgeführt werden.

Wie gesagt, ist dies eine minimale Implementierung. Bei der Suche nach "Vielleicht" NuGet-Paketen werden eine Reihe guter Bibliotheken angezeigt.

Funktionen höherer Ordnung

Eine Funktion höherer Ordnung ist eine Funktion, die eine andere Funktion als Argument akzeptiert oder eine Funktion (oder beide) zurückgibt.

Dies wird im Allgemeinen mit Lambdas durchgeführt, beispielsweise wenn ein Vergleichselement an eine LINQ-Where-Klausel übergeben wird:

var results = data.Where(p => p.Items == 0);

Die Where () - Klausel könnte viele verschiedene Prädikate erhalten, was ihr beträchtliche Flexibilität verleiht.

Das Übergeben einer Methode an eine andere Methode wird auch beim Implementieren des Strategieentwurfsmusters angezeigt. Zum Beispiel können verschiedene Sortiermethoden ausgewählt und an eine Sortiermethode für ein Objekt übergeben werden, abhängig von den Anforderungen zur Laufzeit.

Unveränderliche Sammlungen

Das NuGet-Paket " System.Collections.Immutable enthält unveränderliche Auflistungsklassen.

Artikel erstellen und hinzufügen

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

Erstellen mit dem Builder

Bestimmte unveränderliche Sammlungen verfügen über eine Builder Klasse, mit der große unveränderliche Instanzen kostengünstig erstellt werden können:

var builder = ImmutableList.CreateBuilder<int>(); // returns ImmutableList.Builder
builder.Add(1);
builder.Add(2);
var list = builder.ToImmutable();

Erstellen aus einem vorhandenen IEnumerable

var numbers = Enumerable.Range(1, 5);
var list = ImmutableList.CreateRange<int>(numbers);

Liste aller unveränderlichen Sammlungstypen:



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow