2010-04-18 16 views
20

J'ai regardé Func <> depuis un certain temps, et j'ai réussi à l'éviter (pour l'instant). Mais, maintenant, il semble que je ne peux pas l'éviter pour toujours. Par exemple, j'ai essayé Dynamic Linq, mais presque tout était en termes de Func <>. J'ai essayé un de mes livre (C# 2008/Deitel & Deitel) et aussi MSDN mais je ne l'obtiens pas encore. Ils sautent tous directement dans le sujet.En peu de mots, ce qui peut être dit à propos de Func <>

  1. Que dire (en quelques mots) à propos Func <>
  2. Puis-je obtenir des liens sur le web qui peut me faire commencé à ce sujet?

Merci de nous aider

+1

Le livre de Jon Skeet sur c sharp a quelques excellents chapitres sur ce sujet et pourquoi il est si important pour les fonctions lambda et linq. –

+0

Il n'y a rien d'effrayant, et aucune raison d'éviter, les délégués Func ou génériques - ce sont simplement des délégués qui représentent des méthodes générales qui prennent un certain nombre d'arguments. – thecoop

+0

Vous voudrez peut-être regarder les différentes questions Func sur SO: http://stackoverflow.com/questions/tagged/func –

Répondre

33

Func<> est un délégué générique - il est très pratique à utiliser, car vous n'avez pas besoin de créer votre propre délégué pour chaque combinaison argument/type de retour.
Plus tôt, il fallait écrire quelque chose comme:

public delegate long MyDelegate(int number); 

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

Il fallait publier votre délégué de sorte qu'un utilisateur peut appeler votre méthode correctement. Surtout quand vous avez besoin d'un groupe de délégués différents, vous avez fini par en publier un pour chaque liste d'arguments et le type de retour.
Avec Func<> vous venez d'écrire:

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

Cela signifie la même chose que le premier exemple de code - Func<int, long> définit un délégué qui prend un argument entier et renvoie une valeur à long.

Bien sûr, vous pouvez utiliser des listes de paramètres plus aussi: Func<int, int, bool, long> retournera encore valeur longue alors qu'il faut deux ints et une bool valeur. Si vous souhaitez un délégué sans valeur de retour, vous devrez utiliser Action<>, qui aura void comme type de retour.

EDIT (sur demande): Comment appeler la méthode dans mon exemple:

Pour l'appelant, il n'y a pas de différence entre la solution avec MyDelegate ou Func<>. Dans les deux cas, il a trois options pour appeler la méthode:

utilisant une notation lambda (C# 3.0 nécessaire, probablement la meilleure solution pour de courtes méthodes):

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

En utilisant une méthode anonyme (C# 2.0 nécessaire):

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

Ou en utilisant une vraie méthode comme argument:

Method(myList, Square); 

private static long Square(int number) 
{ 
    return number * number; 
} 
+0

Très belle explination! – John

+0

Bonne explication. Serait bon d'inclure un exemple d'utilisation/d'appel de la méthode. –

+0

@Metro: J'ai ajouté quelques exemples comment appeler la méthode – tanascius

3

Vous pouvez commencer par 101 Linq Samples.

En résumé, Func<> est un délégué où le paramètre de dernier type est le type de retour. Par conséquent, Func<int,bool> est un délégué qui prend un paramètre int et renvoie un bool.

12

Func<...> est une famille de types délégués, qui renvoient une certaine valeur et prennent un certain nombre d'arguments; par exemple:

  • Func<int,bool> est tout simplement quelque chose qui prend un entier et retourne un bool (le retour est toujours à la fin); par exemple un prédicat:

    int[] data = {1,2,3,4,5}; 
    var odd = data.Where(i => i % 2 == 0); 
    
  • Func<string> est une méthode qui renvoie la chaîne, tels que () => "hello world";.

  • Func<DateDtime, TimeSpan, DateTime> pourrait être quelque chose comme (when,howLong) => when + howLong;

De même, il est Action<...> qui fait la même chose mais sans un type de retour.

Il n'y a rien de magique Func<...> - c'est simplement une façon plus simple d'exprimer des délégués, alors que: utiliser des génériques (utiles pour LINQ) ou b: ne pas avoir besoin de chercher quels sont les arguments; si le type de délégué est quelque chose d'obscur (PipeStreamImpersonationWorker par exemple) il peut être difficile de savoir ce qu'il faut; si cela a été exprimé comme le Action comparable il serait clair qu'il ne prend pas de paramètres et renvoie void.

1

Func < ..., T> est délégué. où T est le type de retour, et tous les autres - les paramètres d'entrée.

7

Func<int> (par exemple) est un type (de la façon dont string est un type). Vous l'utilisez donc pour déclarer des variables, des champs, des paramètres, etc.

Il représente un calcul qui peut être fait à chaque fois que vous demandez une réponse:

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

// elsewhere... 

Console.WriteLine(f()); 

Notez comment vous pouvez l'appeler comme une méthode.Il existe plusieurs versions surchargées de Func pour prendre en charge différents nombres de paramètres. Le dernier argument de type est le type de retour.

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

Console.WriteLine(quoteInt(3)); 

Func est un type de délégué. Vous pouvez déclarer le vôtre, mais il est plus facile d'utiliser Func. Lorsque vous voulez retourner void, utilisez Action au lieu de Func. Vous n'avez besoin de déclarer des délégués personnalisés que si vous avez besoin des paramètres out ou ref.

Lors de l'affectation d'un lambda à un Func, vous pouvez vous référer aux variables locales. C'est extrêmement puissant. cela signifie qu'un Func est plus que du code; il a des données. Donc, c'est comme un objet avec une seule méthode (techniquement, la méthode est appelée Invoke et le compilateur appelle implicitement cette méthode lorsque vous appelez un délégué).

La syntaxe () => peut être placée avant toute expression pour dire "ne faites pas cela maintenant, attendez jusqu'à plus tard". Il vous permet d'initialiser un délégué capturant le calcul différé. Et puis la syntaxe () peut être placée après le délégué pour déclencher le calcul. Donc le suffixe () est en quelque sorte le contraire du préfixe () =>.

+0

+1 pour une explication soignée. –

10

Cela pourrait aider. Supposons que chaque fois que vous voyez Func<int, string> vous pensez à vous-même:

interface IFuncIntString 
{ 
    string Invoke(int x); 
} 

C'est, le délégué est un objet qui implémente cette interface. Il a une seule méthode appelée Invoke qui prend un int et retourne une chaîne.

Ajoutez maintenant à cela la fonctionnalité que vous pouvez omettre le "Invoke" sur un appel, et vous avez vous-même un délégué.

+0

C'est comme ça que j'aime penser aux délégués. – kenny

+2

La meilleure façon de l'expliquer à un programmeur Java;) – juharr

1

Si vous avez déjà utilisé l'opérateur => dans C#, et vous l'avez probablement déjà fait, vous avez déjà utilisé Funcs. Vous ne venez pas de les déclarer explicitement.

Donc, si vous écrivez une déclaration comme

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

vous passez un Func<Person, bool> dans la méthode where().

Si vous voulez être verbeux, vous pouvez réécrire cette déclaration comme ceci:

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

Et vous obtiendrez le même résultat.