Возможности использования Func <> для улучшения удобочитаемости кода

Сегодня я наконец "получил" Func<> делегат и увидел, как я могу использовать его, чтобы сделать некоторые из моих менее читаемых запросов LINQ (надеюсь) более читаемый.

Здесь простой пример кода, иллюстрирующий выше, в (очень) тривиальном примере

List<int> numbers = new List<int> { 1, 5, 6, 3, 8, 7, 9, 2, 3, 4, 5, 6, };

// To get the count of those that are less than four we might write:
int lessThanFourCount = numbers.Where(n => n < 4).Count();

// But this can also be written as:
Func<int, bool> lessThanFour = n => n < 4;

int lessThanFourCount = numbers.Where(lessThanFour).Count();

Может ли кто-нибудь еще приводить примеры сценариев, в которых они используют Func<>?

(Обратите внимание, что я не буду защищать использование Func<> в сценарии так же просто, как показано выше, это просто пример, который, надеюсь, делает функциональность Func<> ясной.)

Ответ 1

Я предполагаю, что в этом будет только точка, если вы собираетесь повторно использовать рассматриваемый Func в нескольких местах (и это связано с более чем тривиальной логикой). В противном случае использование стандартного способа кажется намного лучше и прекрасно читаемым.

Ответ 2

Это только более читаемо/полезно, когда/если вы повторно используете его.

Более интересная Lambda для прохождения и futz с, будет Expression<Func<int,bool>>, поскольку вы все равно можете вызвать ее, когда захотите, но вы также можете полностью ее разобрать и поработать с ней.

Его... ну... мертвый сексуальный.

Ответ 3

Я использую делегаты Func и Action для общей обработки исключений. Я часто нахожу, что я строю один и тот же блок try-catch снова и снова, потому что я держу их как можно короче. Делегирование и действие могут уменьшить дублирование кода try-catch.

Действительно простой пример:

...    
DoSomethingPotentiallyBad((x) => x * 2, 0); // both delegates do
DoSomethingPotentiallyBad((x) => 2 / x, 0); // something different ... 
...

    static int DoSomethingPotentiallyBad(Func<int, int> function, int input)
    {
      // ... but get exception handled all the same way  
      try
        {
            return function.Invoke(input);
        }
        catch
        {
            Console.WriteLine("Evaluation failed! Return 0.");
            return 0;
        }
    }

Этот пример очень искусственный и не показывает его силу. Но предположил, что у вас есть что-то вроде операций с базами данных, и вы хотите повторить каждую операцию сбойной базы данных 5 раз (что связано с использованием флагов, цикла finally-block и while) и хотите, чтобы один и тот же журнал приложений использовался, тогда у вас есть только один место для размещения блока try-catch: это метод, который принимает аргумент Func или Action в качестве аргумента. Код, который обрабатывает бизнес-логику, почти свободен от этих блоков try-catch и, следовательно, более читабельен.

Для более подробного примера и более подробного описания смотрите: Политика действий

Ответ 4

Обычно я делаю это только тогда, когда хочу повторно использовать делегат. В противном случае, не часто.

Ответ 5

Как насчет принятия параметра Func<> в ваших собственных методах, когда это необходимо, чтобы сделать их более гибкими и расширяемыми? Например:

public static string ToDelimitedString<T>(
    this IEnumerable<T> source, Func<T, string> converter, string separator)
{
    // error checking removed for readability

    StringBuilder sb = new StringBuilder();
    foreach (T item in source)
    {
        sb.Append(converter(item));
        sb.Append(separator);
    }

    return (sb.Length > 0) ?
        sb.ToString(0, sb.Length - separator.Length) : string.Empty;
}

// ...

List<int> e1 = new List<int> { 1, 2, 3 };
// convert to "2|4|6"
string s1 = e1.ToDelimitedString(x => (x * 2).ToString(), "|");

DateTime[] e2 = new DateTime[]
    { DateTime.Now.AddDays(-100), DateTime.Now, DateTime.Now.AddDays(100) };
// convert to "Tuesday, Thursday, Saturday"
string s2 = e2.ToDelimitedString(x => x.DayOfWeek.ToString(), ", ");

List<MyCustomType> e3 = new List<MyCustomType>
    { new MyCustomType("x"), new MyCustomType("y"), new MyCustomType("z") };
// convert to "X and Y and Z"
string s3 = e3.ToDelimitedString(x => x.Name.ToUpper(), " and ");

Ответ 6

В большинстве случаев НЕ.