Возможности использования Func <> для улучшения читабельности кода - PullRequest
4 голосов
/ 30 марта 2009

Сегодня я наконец-то «получил» делегата 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<>.)

Ответы [ 6 ]

6 голосов
/ 30 марта 2009

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

4 голосов
/ 30 марта 2009

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

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

Это ... ну ... очень сексуально.

2 голосов
/ 30 марта 2009

Я использую делегаты 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 и цикл while) и хотите, чтобы то же самое входило в журнал всего приложения. тогда у вас есть только одно место для размещения этого блока try-catch: это метод, который принимает делегат Func или Action в качестве аргумента. Код, который обрабатывает бизнес-логику, практически не содержит блоки try-catch и поэтому более читабелен.

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

1 голос
/ 02 апреля 2009

Как насчет принятия параметра 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 ");
1 голос
/ 30 марта 2009

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

0 голосов
/ 30 марта 2009

Большую часть времени НЕ.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...