2010-09-02 8 views
80

Was ist Func<> und wofür wird es verwendet?Was ist Func, wie und wann wird es verwendet?

+2

Es ist nur eine Abkürzung für Delegaten mit einer bestimmten Signatur. Um die folgenden Antworten vollständig zu verstehen, müssen Sie die Delegierten verstehen ;-) –

+1

In der Antwort von @Oded steht: 'Wenn Sie eine Funktion haben, die abhängig von den Parametern unterschiedliche Typen zurückgeben muss, können Sie einen Func-Delegaten verwenden, Angabe der Rückgabetyp.' – Lijo

Antwort

57

Func<T> ist ein vordefinierter Delegattyp für eine Methode, die einen Wert vom Typ T zurückgibt.

Mit anderen Worten können Sie diesen Typ verwenden, um auf eine Methode zu verweisen, die einen Wert von T zurückgibt. Z.B.

public static string GetMessage() { return "Hello world"; } 

können wie diese

Func<string> f = GetMessage; 
+0

Aber es kann auch eine statische ein-Argument-Funktion =) –

+2

@ Ark-Kun Nein, das ist nicht korrekt. Die Definition von 'Func ' ist 'Delegat TResult Func ()'. Keine Argumente. 'Func ' wäre eine Funktion, die ein Argument benötigt. –

+3

Nein, ich habe Recht. 'static int OneArgFunc (diese Zeichenfolge i) {return 42; } '' Func f = "foo" .OneArgFunc; '. =) –

9

Func<T1,R> und die anderen vordefinierten generisch Func Delegierten (Func<T1,T2,R>, Func<T1,T2,T3,R> und anderen) sind generische Delegaten verwiesen werden, die den Typen des letzten generischen Parameters zurückzukehren.

Wenn Sie eine Funktion haben, die abhängig von den Parametern unterschiedliche Typen zurückgeben muss, können Sie einen Func Delegaten verwenden, der den Rückgabetyp angibt.

66

Betrachten Sie es als Platzhalter. Es kann sehr nützlich sein, wenn Sie Code haben, der einem bestimmten Muster folgt, aber nicht an eine bestimmte Funktionalität gebunden sein muss.

Betrachten Sie beispielsweise die Erweiterungsmethode Enumerable.Select.

  • Die Muster sind: für jedes Element in einer Sequenz, wählen einen Wert aus diesem Elemente (z.B. eine Eigenschaft) und eine neue Folge dieser Werte aus erstellen.
  • Der Platzhalter ist: einige Auswahlfunktion, die tatsächlich die Werte für die oben beschriebene Sequenz erhält.

Diese Methode benötigt eine Func<T, TResult> anstelle einer konkreten Funktion. Dies ermöglicht die Verwendung in beliebigen Kontext, in dem das obige Muster gilt.

Also zum Beispiel sagen, ich habe eine List<Person> und ich möchte nur den Namen jeder Person in der Liste. Ich kann dies tun:

var names = people.Select(p => p.Name); 

Oder sagen, ich will das Alter jeder Person:

var ages = people.Select(p => p.Age); 

Sogleich, können Sie sehen, wie ich in der Lage war, die gleichen Code zu nutzen, repräsentieren Muster (mit Select) mit zwei verschiedenen Funktionen (p => p.Name und p => p.Age).

Die Alternative wäre, eine andere Version von Select jedes Mal zu schreiben, wenn Sie eine Sequenz für eine andere Art von Wert scannen wollten.So um die gleiche Wirkung zu erzielen wie oben, würde ich brauchen:

// Presumably, the code inside these two methods would look almost identical; 
// the only difference would be the part that actually selects a value 
// based on a Person. 
var names = GetPersonNames(people); 
var ages = GetPersonAges(people); 

mit einem Delegierten als Platzhalter fungieren, ich befreie mich aus mit dem gleichen Muster immer und immer wieder in Fällen wie diesen zu schreiben.

47

Func<T1, T2, ..., Tn, Tr> stellt eine Funktion dar, die Argumente (T1, T2, ..., Tn) akzeptiert und Tr zurückgibt.

Zum Beispiel, wenn Sie eine Funktion:

double sqr(double x) { return x * x; } 

Sie es als eine Art funktions Variablen speichern könnte:

Func<double, double> f1 = sqr; 
Func<double, double> f2 = x => x * x; 

Und dann genau verwenden, wie Sie sqr verwenden würden:

f1(2); 
Console.WriteLine(f2(f1(4))); 

usw.

Denken Sie jedoch daran, dass es sich um einen Delegierten handelt. Weitere Informationen finden Sie in der Dokumentation.

+0

Ausgezeichnete Antwort, aber für die Kompilierung von Stichwort Statisch ist erforderlich – boctulus

5

Es ist nur ein vordefinierter generischer Delegat. Mit ihr müssen Sie nicht jeden Delegierten deklarieren. Es gibt einen anderen vordefinierten Delegaten, Action<T, T2...>, der identisch ist, aber void zurückgibt.

3

Ich finde Func sehr nützlich, wenn ich eine Komponente erstellen, die "on the fly" personalisiert werden muss.

Nehmen Sie dieses sehr einfache Beispiel: eine PrintListToConsole Komponente.

Ein sehr einfaches Objekt, das diese Liste von Objekten auf der Konsole ausgibt. Sie möchten den Entwickler, der es verwendet, die Ausgabe personalisieren lassen.

Zum Beispiel, Sie wollen ihn eine bestimmte Art von Zahlenformat definieren und so weiter.

Ohne Func

Zuerst müssen Sie eine Schnittstelle für eine Klasse erstellen, die die Eingabe nehmen und die Zeichenfolge zu erzeugen, um die Konsole zu drucken.

interface PrindListConsoleRender<T> { 
    String Render(T input); 
} 

Dann müssen Sie die Klasse PrintListToConsole erstellen, die die vorherige geschaffene Schnittstelle nehmen und sie über jedes Element der Liste verwenden.

class PrintListToConsole<T> { 

    private PrindListConsoleRender<T> _renderer; 

    public void SetRenderer(PrindListConsoleRender<T> r) { 
     // this is the poin where I can personalize the render mechanism 
     _renderer = r; 
    } 

    public void PrintToConsole(List<T> list) { 
     foreach (var item in list) { 
      Console.Write(_renderer.Render(item)); 
     } 
    } 
} 

Der Entwickler, die Ihre Komponente verwenden müssen müssen:

1), um die Schnittstelle implementieren

2) passiert die wirkliche Klasse des PrintListToConsole

class myrenderer : PrindListConsoleRender<int> { 
    public String Render(int input) { 
     return "Number: " + input; 
    } 
} 

class Program { 
    static void Main(string[] args) { 
     var list = new List<int> { 1, 2, 3 }; 
     var printer = new PrintListToConsole<int>(); 
     printer.SetRenderer(new myrenderer()); 
     printer.PrintToConsole(list); 
     string result = Console.ReadLine(); 
    } 
} 

Func Verwendung es ist viel einfacher

innerhalb der Komponente einen Parameter des Typs Func definieren, dass eine Schnittstelle einer Funktion darstellen, die einen Eingabeparameter des Typs T nehmen und eine Zeichenfolge zurück (die Ausgabe für die Konsole)

class PrintListToConsole<T> { 

    private Func<T, String> _renderFunc; 

    public void SetRenderFunc(Func<T, String> r) { 
     // this is the point where I can set the render mechanism 
     _renderFunc = r; 
    } 

    public void StampaFunc(List<T> list) { 
     foreach (var item in list) { 
      Console.Write(_renderFunc(item)); 
     } 
    } 
} 

Wenn der Entwickler Verwendung Ihre Komponente es einfach an die Komponente die Implementierung des Func-Typs übergeben, das ist eine Funktion, die die Ausgabe für die Konsole erstellen.

class Program { 
    static void Main(string[] args) { 
     var list = new Array[1, 2, 3]; 
     var printer = new PrintListToConsole<int>(); 
     printer.SetRenderFunc((o) => "Number:" + o); 
     string result = Console.ReadLine(); 
    } 
} 

Func können Sie eine generische Methode Schnittstelle on the fly definieren. Sie definieren, welcher Typ die Eingänge und welcher Typ der Ausgang ist. Einfach und prägnant.

+1

Vielen Dank für das Posten dieses Marco. Es hat mir wirklich geholfen. Ich habe versucht, Func für eine Weile zu verstehen und auch aktiv in meiner Programmierung zu verwenden. In diesem Beispiel wird der Pfad gelöscht. Ich musste die StampaFunc-Methode hinzufügen, die im ursprünglichen Code weggelassen wurde, wodurch sie nicht angezeigt werden konnte. –

Verwandte Themen