Поиск…


Func и Action

Func предоставляет держатель для параметризованных анонимных функций. Ведущими типами являются входы, а последний тип всегда является возвращаемым значением.

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

Объекты Action подобны void-методам, поэтому они имеют только тип ввода. В стек оценки нет результата.

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

неизменность

Непрерывность распространена в функциональном программировании и редка в объектно-ориентированном программировании.

Создайте, например, тип адреса с изменчивым состоянием:

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

Любой фрагмент кода может изменить любое свойство в вышеуказанном объекте.

Теперь создайте неизменяемый тип адреса:

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

Имейте в виду, что наличие коллекций только для чтения не учитывает неизменность. Например,

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

не является неизменным, так как пользователь объекта может изменять коллекцию (добавлять или удалять из нее элементы). Чтобы сделать его неизменным, нужно либо использовать интерфейс, такой как IEnumerable, который не предоставляет методы для добавления, или сделать его 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());   

С неизменяемым объектом мы имеем следующие преимущества:

  • Он будет находиться в известном состоянии (другой код не может его изменить).
  • Это поточно-безопасный.
  • Конструктор предлагает одно место для проверки.
  • Зная, что объект не может быть изменен, код легче понять.

Избегайте ссылок на Null

Разработчики C # получают множество нулевых ссылочных исключений. Разработчики F # не потому, что у них есть тип Option. Тип Option <> (некоторые предпочитают, возможно, <> как имя), предоставляет тип возврата Some и a None. Он делает явным, что метод может возвращать нулевую запись.

Например, вы не можете прочитать следующее и знать, если вам придется иметь дело с нулевым значением.

var user = _repository.GetUser(id);

Если вы знаете о возможном нуле, вы можете ввести код шаблона, чтобы справиться с ним.

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

Что делать, если вместо этого есть опция <>?

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

Теперь код делает явным, что у нас может быть запись «Нет», и требуется шаблонный код для проверки Some или None:

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

Следующий метод показывает, как вернуть параметр <>

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

Ниже приведена минимальная реализация 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);
    }
}

Чтобы продемонстрировать вышеописанное, избегайте Null.csx с C # REPL.

Как было сказано, это минимальная реализация. Поиск пакетов «Возможно» NuGet приведет к появлению ряда хороших библиотек.

Функции более высокого порядка

Функция более высокого порядка - это функция, которая принимает другую функцию в качестве аргумента или возвращает функцию (или и то, и другое).

Это обычно делается с lambdas, например, при передаче предиката в предложение LINQ Where:

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

Предложение Where () может принимать множество разных предикатов, что дает ему большую гибкость.

Передача метода в другой метод также наблюдается при реализации шаблона проектирования стратегии. Например, различные методы сортировки могут быть выбраны и переданы методу Сортировки на объект в зависимости от требований во время выполнения.

Неизменяемые коллекции

Пакет System.Collections.Immutable NuGet предоставляет неизменные классы коллекции.

Создание и добавление элементов

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

Создание с использованием строителя

У некоторых неизменяемых коллекций есть внутренний класс Builder который можно использовать для дешевого построения больших неизменяемых экземпляров:

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

Создание из существующего IEnumerable

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

Список всех неизменяемых типов коллекций:



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow