В двух словах, что можно сказать о Func‹›

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

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

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


person Richard77    schedule 18.04.2010    source источник
comment
В книге Джона Скита по C Sharp есть несколько отличных глав, посвященных этому и тому, почему это так важно для лямбда-функций и linq.   -  person James Westgate    schedule 18.04.2010
comment
Нет ничего страшного и нет причин избегать использования Func или универсальных делегатов — это просто делегаты, представляющие общие методы, принимающие определенное количество аргументов.   -  person thecoop    schedule 18.04.2010
comment
Вы можете просмотреть различные вопросы Func на SO: stackoverflow.com/questions/tagged/func   -  person Metro Smurf    schedule 18.04.2010
comment
возможный дубликат Что хорошего в делегате Func‹›?   -  person nawfal    schedule 31.03.2013


Ответы (7)


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<>. В обоих случаях у него есть три варианта вызова метода:

Используя лямбда-нотацию (требуется С# 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;
}
person tanascius    schedule 18.04.2010
comment
Хорошее объяснение. Было бы неплохо включить пример использования/вызова метода. - person Metro Smurf; 18.04.2010
comment
@Metro: я добавил несколько примеров вызова метода - person tanascius; 18.04.2010
comment
Связанный отличный ответ: stackoverflow.com/questions/ 319789/ - person halfbit; 24.01.2011

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

person Marc Gravell    schedule 18.04.2010

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

interface IFuncIntString
{
    string Invoke(int x);
}

То есть делегат — это объект, реализующий этот интерфейс. У него есть единственный метод Invoke, который принимает целое число и возвращает строку.

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

person Eric Lippert    schedule 18.04.2010
comment
Вот как мне нравится думать о делегатах. - person kenny; 18.04.2010
comment
Лучший способ объяснить это Java-программисту;) - person juharr; 18.04.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, и компилятор неявно вызывает этот метод для вас, когда вы вызываете делегат).

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

person Daniel Earwicker    schedule 18.04.2010

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

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

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

person Oded    schedule 18.04.2010

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

person Ilya Smagin    schedule 18.04.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);

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

person Jeremy Frey    schedule 18.04.2010