← Назад к вопросам

Что такое Func?

1.3 Junior🔥 161 комментариев
#Основы C# и .NET

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое Func в C#?

Func — это один из встроенных обобщённых делегатов (generic delegates) в C#, предназначенный для инкапсуляции методов, которые возвращают значение (в отличие от Action, который представляет методы без возвращаемого значения). Он является частью пространства имён System и активно используется в парадигмах функционального программирования, LINQ, асинхронных операциях и обработке событий.

Основные характеристики Func

  1. Сигнатура делегата: Последний параметр типа в Func всегда представляет возвращаемый тип метода, а предыдущие — типы входных параметров. Например:

    • Func<TResult> — метод без параметров, возвращающий значение типа TResult.
    • Func<T1, TResult> — метод с одним параметром типа T1, возвращающий TResult.
    • Func<T1, T2, TResult> — метод с двумя параметрами, возвращающий TResult.
    • Максимальное количество входных параметров — 16 (до .NET 7; в современных версиях можно использовать больше через System.Func).
  2. Использование в коде: Func позволяет передавать методы как объекты, сохранять их в переменных и вызывать позже. Это основа для реализации колбэков, стратегий и отложенных вычислений.

Примеры использования Func

Пример 1: Базовый вызов

using System;

class Program
{
    static void Main()
    {
        // Func с одним параметром и возвращаемым значением
        Func<int, string> convertToString = x => $"Число: {x}";
        string result = convertToString(42); // Вызов делегата
        Console.WriteLine(result); // Вывод: "Число: 42"
    }
}

Пример 2: Передача Func как параметра (паттерн Стратегия)

using System;

class Calculator
{
    public int Calculate(Func<int, int, int> operation, int a, int b)
    {
        return operation(a, b); // Применение переданной операции
    }
}

class Program
{
    static void Main()
    {
        var calc = new Calculator();
        
        // Передача лямбда-выражения для сложения
        int sum = calc.Calculate((x, y) => x + y, 10, 5);
        Console.WriteLine($"Сумма: {sum}"); // 15
        
        // Передача метода умножения
        int product = calc.Calculate(Multiply, 10, 5);
        Console.WriteLine($"Произведение: {product}"); // 50
    }

    static int Multiply(int a, int b) => a * b;
}

Пример 3: Использование в LINQ

В LINQ Func часто применяется для селекторов (selectors) и предикатов (predicates):

using System;
using System.Linq;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        var numbers = new List<int> { 1, 2, 3, 4, 5 };
        
        // Func как предикат для фильтрации
        Func<int, bool> isEven = x => x % 2 == 0;
        var evenNumbers = numbers.Where(isEven).ToList();
        
        // Func как селектор для преобразования
        Func<int, string> toSquareString = x => $"Квадрат: {x * x}";
        var squares = numbers.Select(toSquareString).ToList();
        
        Console.WriteLine(string.Join(", ", evenNumbers)); // 2, 4
        Console.WriteLine(string.Join(", ", squares)); // Квадрат: 1, Квадрат: 4, ...
    }
}

Пример 4: Кэширование вычислений (Memoization)

Func может использоваться для оптимизации повторных вычислений:

using System;
using System.Collections.Generic;

class Program
{
    static Func<int, int> Memoize(Func<int, int> func)
    {
        var cache = new Dictionary<int, int>();
        return x =>
        {
            if (cache.ContainsKey(x)) return cache[x];
            var result = func(x);
            cache[x] = result;
            return result;
        };
    }

    static void Main()
    {
        Func<int, int> factorial = null;
        factorial = n => (n <= 1) ? 1 : n * factorial(n - 1);
        
        var memoizedFactorial = Memoize(factorial);
        Console.WriteLine(memoizedFactorial(5)); // 120 (вычисляется)
        Console.WriteLine(memoizedFactorial(5)); // 120 (берётся из кэша)
    }
}

Преимущества использования Func

  • Универсальность: Позволяет работать с методами любого типа (с возвращаемым значением) через единый интерфейс.
  • Гибкость: Упрощает реализацию паттернов (Стратегия, Фабрика) и колбэков.
  • Интеграция с LINQ: Является основой для запросов к коллекциям и базам данных (например, в Entity Framework).
  • Лямбда-выражения: Позволяет использовать краткий синтаксис для определения методов "на лету".

Ограничения и лучшие практики

  • Производительность: При частом использовании в высоконагруженных сценариях следует учитывать накладные расходы на вызов делегата (хотя они минимальны).
  • Читаемость: В сложных сигнатурах (например, Func<int, string, bool, decimal, MyClass>) код может стать менее понятным. В таких случаях рекомендуется объявлять пользовательские делегаты или использовать record/class.
  • Null-безопасность: Перед вызовом Func стоит проверять его на null, чтобы избежать NullReferenceException. В современных версиях C# можно использовать условный вызов: myFunc?.Invoke(...).

Отличие от Action и Predicate

  • Action: Делегат для методов без возвращаемого значения (void). Пример: Action<string> — метод, принимающий string и ничего не возвращающий.
  • Predicate: Частный случай Func, возвращающий bool. Пример: Predicate<int> эквивалентен Func<int, bool>. В современных версиях C# чаще используется Func<int, bool> для единообразия.

Заключение

Func — это мощный инструмент в арсенале C#-разработчика, который стирает границы между данными и поведением. Он активно применяется в LINQ, асинхронном программировании (например, в комбинации с Task), обработке событий и реализации функциональных паттернов. Понимание Func критически важно для написания гибкого, поддерживаемого и выразительного кода, особенно в современных приложениях, где декларативный стиль и функциональные подходы становятся стандартом.