Szukaj…


Func and Action

Func zapewnia uchwyt dla sparametryzowanych funkcji anonimowych. Typami wiodącymi są dane wejściowe, a ostatnim typem jest zawsze wartość zwracana.

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

Obiekty akcji są jak puste metody, więc mają tylko typ wejściowy. Żaden wynik nie jest umieszczany na stosie oceny.

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

Niezmienność

Niezmienność jest powszechna w programowaniu funkcjonalnym, a rzadko w programowaniu obiektowym.

Utwórz na przykład typ adresu ze stanem zmiennym:

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

Każdy fragment kodu może zmienić dowolną właściwość w powyższym obiekcie.

Teraz utwórz niezmienny typ adresu:

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

Pamiętaj, że posiadanie kolekcji tylko do odczytu nie respektuje niezmienności. Na przykład,

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

nie jest niezmienne, ponieważ użytkownik obiektu może zmieniać kolekcję (dodawać lub usuwać z niej elementy). Aby uczynić go niezmiennym, należy albo użyć interfejsu takiego jak IEnumerable, który nie ujawnia metod dodawania, lub uczynić z niego 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());   

Dzięki niezmiennemu obiektowi mamy następujące zalety:

  • Będzie w znanym stanie (inny kod nie może tego zmienić).
  • Jest bezpieczny dla wątków.
  • Konstruktor oferuje jedno miejsce do sprawdzania poprawności.
  • Wiedza, że obiektu nie można zmienić, ułatwia zrozumienie kodu.

Unikaj pustych referencji

Programiści C # otrzymują wiele wyjątków o zerowej referencji. Programiści F # nie, ponieważ mają typ opcji. Typ Option <> (niektórzy wolą być może <> jako nazwa) zapewnia typ zwracania Some i None. Wyjaśnia, że metoda może wkrótce zwrócić rekord zerowy.

Na przykład nie możesz przeczytać poniższych informacji i wiedzieć, czy będziesz musiał poradzić sobie z wartością zerową.

var user = _repository.GetUser(id);

Jeśli wiesz o możliwym zeru, możesz wprowadzić kod bojlera, aby sobie z tym poradzić.

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

Co, jeśli zamiast tego zostanie zwrócona opcja <>?

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

Kod wyjaśnia teraz, że może zostać zwrócony rekord Brak i wymagany jest kod płytki kontrolnej, aby sprawdzić, czy Niektóre lub Brak:

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

Poniższa metoda pokazuje, jak zwrócić opcję <>

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

Oto minimalna implementacja opcji <>.

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

Aby zademonstrować powyższe, można uruchomić unikNull.csx z C # REPL.

Jak wspomniano, jest to minimalna implementacja. Wyszukiwanie pakietów „Może” NuGet spowoduje wyświetlenie szeregu dobrych bibliotek.

Funkcje wyższego rzędu

Funkcja wyższego rzędu to taka, która przyjmuje inną funkcję jako argument lub zwraca funkcję (lub obie te funkcje).

Zwykle robi się to z lambdas, na przykład przy przekazywaniu predykatu do klauzuli LINQ Where:

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

Klauzula Where () może otrzymać wiele różnych predykatów, co zapewnia jej znaczną elastyczność.

Przekazywanie metody do innej metody jest również widoczne przy wdrażaniu wzorca projektowania strategii. Na przykład można wybierać różne metody sortowania i przekazywać je do metody sortowania obiektu w zależności od wymagań w czasie wykonywania.

Niezmienne kolekcje

Pakiet System.Collections.Immutable NuGet zapewnia niezmienne klasy kolekcji.

Tworzenie i dodawanie elementów

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

Tworzenie za pomocą konstruktora

Niektóre niezmienne kolekcje mają wewnętrzną klasę Builder której można użyć do taniego budowania dużych niezmiennych instancji:

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

Tworzenie z istniejącego IEnumerable

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

Lista wszystkich niezmiennych typów kolekcji:



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow