В двух словах, что можно сказать о Func <> - PullRequest
20 голосов
/ 18 апреля 2010

Я уже некоторое время вижу Func <> , и мне удается этого избежать (пока). Но теперь, похоже, я не могу увернуться от этого вечно. Например, я попробовал Dynamic Linq, но почти все было в терминах Func <>. Я попробовал одну из своих книг (C # 2008 / Deitel & Deitel), а также MSDN, но пока не получаю. Все они прыгают прямо в теме.

  1. Что можно сказать (в двух словах) о Func <>
  2. Могу ли я получить ссылки в Интернете, которые помогут мне начать работу по этому вопросу?

Спасибо за помощь

Ответы [ 7 ]

33 голосов
/ 18 апреля 2010

Func<> - универсальный делегат - его очень удобно использовать, потому что вам не нужно создавать свой собственный делегат для каждой комбинации аргумент / возвращаемый тип.
Ранее вам приходилось писать что-то вроде:

public delegate long MyDelegate( int number );

public void Method( IEnumerable<int> list, MyDelegate myDelegate )
{
    foreach( var number in list )
    {
        myDelegate( number );
    }
}

Вы должны были опубликовать свой делегат, чтобы пользователь мог правильно вызвать ваш метод. Особенно, когда вам нужно несколько разных делегатов, вы заканчиваете тем, что публикуете по одному для каждого списка аргументов и типа возвращаемого значения.
С Func<> вы просто пишете:

public void Method( IEnumerable<int> list, Func<int, long> myDelegate )
{
    foreach( var number in list )
    {
        myDelegate( number );
    }
}

Это означает то же, что и в первом примере кода - Func<int, long> определяет делегата, который принимает один целочисленный аргумент и возвращает длинное значение.

Конечно, вы также можете использовать более длинные списки параметров: Func<int, int, bool, long> будет по-прежнему возвращать значение long , в то время как оно принимает два ints и bool значение , Если вы хотите делегата без возвращаемого значения, вы должны будете использовать Action<>, который будет иметь void в качестве типа возврата.

РЕДАКТИРОВАТЬ (по запросу): Как вызвать метод в моем примере :

Для вызывающего абонента нет разницы между решением с MyDelegate или Func<>. В обоих случаях у него есть три варианта вызова метода:

Использование лямбда-нотации (требуется C # 3.0, вероятно, лучшее решение для коротких методов):

Method( myList, i => i * i );

Используя анонимный метод (требуется C # 2.0):

Method( myList, delegate( int i )
{
    return i * i;
} );

Или используя реальный метод в качестве аргумента:

Method( myList, Square );

private static long Square( int number )
{
    return number * number;
}
12 голосов
/ 18 апреля 2010

Func<...> - это семейство типов делегатов, которые возвращают некоторое значение и принимают некоторое количество аргументов; например:

  • Func<int,bool> это просто что-то, что принимает int и возвращает bool (возврат всегда в конце); например предикат:

    int[] data = {1,2,3,4,5};
    var odd = data.Where(i => i % 2 == 0);
    
  • Func<string> - это метод, который возвращает строку, например () => "hello world";.

  • Func<DateDtime, TimeSpan, DateTime> может быть что-то вроде (when,howLong) => when + howLong;

Аналогично существует Action<...>, который делает то же самое, но без типа возврата.

В Func<...> нет ничего волшебного - это просто более простой способ выражения делегатов, в то время как a: использование обобщений (полезно для LINQ) или b: не нужно, чтобы вы искали аргументы; если тип делегата неясен (например, PipeStreamImpersonationWorker), может быть трудно понять, что для этого нужно; если бы это было выражено как сопоставимое Action, было бы ясно, что оно не принимает никаких параметров и возвращает void.

10 голосов
/ 18 апреля 2010

Это может помочь. Предположим, каждый раз, когда вы видите Func<int, string>, вы думаете себе:

interface IFuncIntString
{
    string Invoke(int x);
}

То есть делегат - это объект, который реализует этот интерфейс. Он имеет единственный метод Invoke, который принимает int и возвращает строку.

Теперь добавьте к этой функции, что вы можете опустить «Вызов» при вызове, и вы получите делегата.

7 голосов
/ 18 апреля 2010

Func<int> (например) - это тип (в том смысле, как string - это тип). Таким образом, вы используете его для объявления переменных, полей, параметров и т. Д.

Он представляет собой вычисление, которое может быть выполнено всякий раз, когда вы запрашиваете ответ:

Func<int> f = () => DateTime.Now.Second;

// elsewhere...

Console.WriteLine( f() );

Обратите внимание, как вы можете вызвать его так же, как метод. Существует много перегруженных версий Func для поддержки различного количества параметров. Последний аргумент типа - это тип возвращаемого значения.

Func<int, string> quoteInt = n => "\"" + n + "\"";

Console.WriteLine( quoteInt(3) );

Func - тип делегата. Вы можете заявить о себе, но проще использовать Func. Если вы хотите вернуть void, используйте Action вместо Func. Вам нужно только объявить пользовательские делегаты, если вам нужны out или ref параметры.

При назначении лямбды на Func вы можете ссылаться на локальные переменные. Это очень сильно; это означает, что Func - это больше, чем просто код; у него есть данные. Таким образом, это похоже на объект с единственным методом (который технически называется - метод называется Invoke, и компилятор неявно вызывает этот метод для вас при вызове делегата).

Синтаксис () => может быть помещен перед любым выражением, чтобы сказать «не делай этого сейчас, откладывай на потом». Это позволяет вам инициализировать делегата, фиксирующего отложенные вычисления. И затем синтаксис () может быть помещен после делегата для фактического запуска вычисления. Таким образом, суффикс () является своего рода противоположностью префикса () =>.

3 голосов
/ 18 апреля 2010

Вы можете начать с 101 образцов Linq .

Короче говоря, Func<> - это делегат, где последним параметром типа является тип возвращаемого значения.

Итак, Func<int,bool> - это делегат, который принимает параметр int и возвращает bool.

1 голос
/ 18 апреля 2010

Если вы когда-либо использовали оператор => в c #, и, возможно, вы уже использовали, вы уже использовали Funcs. Вы просто явно не объявляли их.

Итак, если вы напишите заявление типа

var peopleWhoLikeBlue = people.Where(person => person.FavoriteColor == "Blue");

вы передаете Func<Person, bool> в метод Where ().

Если вы хотите быть многословным, вы можете переписать это утверждение так:

Func<Person, bool> favoriteColorIsBlue = person => person.FavoriteColor == "Blue";
var peopleWhoLikeBlue = people.Where(favoriteColorIsBlue);

И вы получите тот же результат.

1 голос
/ 18 апреля 2010

Func <..., T> является делегатом. где T - тип возврата, а все остальные - входные параметры.

...