2010-12-19 3 views
58

Heute habe ich über erklärt dies dachte:Erstellen von Delegierten manuell vs mit Action/Func Delegierten

private delegate double ChangeListAction(string param1, int number); 

aber, warum dies nicht nutzen:

private Func<string, int, double> ChangeListAction; 

oder wenn ChangeListAction würde keinen Rückgabewert haben könnte ich Verwendung:

private Action<string,int> ChangeListAction; 

also wo ist der Vorteil in einen Delegaten mit demerklärt 10 Schlüsselwort?

Ist es wegen .NET 1.1, und mit .NET 2.0 kam Action<T> und mit .NET 3.5 kam Func<T>?

Antwort

46

Der Vorteil ist Klarheit. Indem dem Typ ein expliziter Name gegeben wird, ist es für den Leser klarer, was er tut.

Es wird Ihnen auch helfen, wenn Sie den Code schreiben. Ein Fehler wie folgt aus:

cannot convert from Func<string, int, double> to Func<string, int, int, double> 

ist weniger nützlich als eine, die sagt:

cannot convert from CreateListAction to UpdateListAction 

Es bedeutet auch, dass, wenn Sie zwei verschiedene Delegierte, die gleichen Typen von Parametern von denen beide nehmen, aber konzeptionell tun Zwei völlig verschiedene Dinge, der Compiler kann sicherstellen, dass Sie nicht versehentlich einen verwenden können, wo Sie den anderen meinen.

+1

Die Func hat einen Namen wie Func also sollte der Compiler nicht sagen: Kann nicht von Func1Name zu Func2Name konvertieren, dann wäre es nicht weniger hilfreich. – Elisabeth

10

Das explizite Deklarieren eines Delegaten kann bei einigen Typprüfungen helfen. Der Compiler kann sicherstellen, dass der Delegat, der der Variablen zugewiesen ist, als ChangeListAction und nicht als zufällige Aktion verwendet werden soll, die mit der Signatur kompatibel ist.

Der wahre Wert der Deklaration eines eigenen Delegaten ist jedoch, dass er eine semantische Bedeutung hat. Eine Person, die den Code liest, weiß, was der Delegierte mit seinem Namen macht. Stellen Sie sich vor, Sie hätten eine Klasse mit drei int-Feldern, aber stattdessen haben Sie ein Array von drei int-Elementen deklariert. Das Array kann dasselbe tun, aber die Namen der Felder bringen semantische Informationen, die für die Entwickler nützlich sind.

Sie sollten die Delegierten Func, Predicate und Action verwenden, wenn Sie eine allgemeine Bibliothek wie LINQ entwerfen. In diesem Fall haben die Delegierten keine vordefinierte Semantik, außer der Tatsache, dass sie ausgeführt und ausgeführt werden oder als Prädikat verwendet werden.

Auf einer Randnotiz gibt es eine ähnliche Abwägungsproblem mit Tuple vs anonymer Typ vs deklarieren Sie Ihre eigene Klasse. Sie könnten einfach alles in ein Tupel stecken, aber dann sind die Eigenschaften nur Item1, Item2, was nichts über die Verwendung des Typs sagt.

5

Deklarieren Sie den Delegierten explizit, wenn Sie zu viele Parameter in der Func/Action erhalten, sonst müssen Sie immer wieder zurückschauen: "Was ist der 2nd int wieder?"

7

Da einige Antworten erwähnen, der Gewinn ist Klarheit, Sie benennen den Typ und so wird es einfacher für einen Benutzer Ihrer API zu verstehen. Ich würde sagen - in den meisten Fällen - Delegatentypen für Ihre öffentlichen APIs deklarieren, aber es ist ganz in Ordnung, Func<?,?> intern zu verwenden.

Ein großer Vorteil des Deklarierens des Delegattyps, der in den anderen Antworten nicht erwähnt wird, ist, dass neben der Angabe eines Namens auch die Parameter benannt werden, was die Benutzerfreundlichkeit massiv erhöht.

59

Das Aufkommen von Action und Func Familie von Delegierten hat benutzerdefinierte Delegaten weniger verwendet, aber das letztere findet weiterhin verwendet. Vorteile von benutzerdefiniert Delegierten sind:

  1. Wie andere haben darauf vermittelt Absicht klar im Gegensatz zu generischen Action und Func (Patrik hat einen sehr guten Punkt, um sinnvolle Parameternamen).

  2. Sie können ref/out Parameter im Gegensatz zu den anderen zwei generischen Delegaten angeben. Für zB können Sie

    public delegate double ChangeListAction(out string p1, ref int p2); 
    

    aber nicht

    Func<out string, ref int, double> ChangeListAction; 
    
  3. Auch mit benutzerdefinierten Delegierten haben Sie schreiben müssen ChangeListAction (ich meine die Definition) nur einmal irgendwo in Ihrem Code-Basis, während, wenn Sie definiere nicht, dass du überall überall Func<string, int, double> herumstreuen musst. Das Ändern der Signatur wird im letzteren Fall ein Ärger sein - ein schlimmer Fall, wenn man nicht trocken ist.

  4. Kann optionale Parameter haben.

    public delegate double ChangeListAction(string p1 = "haha", int p2); 
    

    aber nicht

    Func<string, int, double> ChangeListAction = (p1 = "haha", p2) => (double)p2; 
    
  5. Sie können params Schlüsselwort für Parameter einer Methode, nicht so mit Action/Func haben.

    public delegate double ChangeListAction(int p1, params string[] p2); 
    

    aber nicht

    Func<int, params string[], double> ChangeListAction; 
    
  6. Nun, du, wenn Sie wirklich kein Glück und brauchen Parameter mehr als 16 (im Moment) :)


Zu den Vorzügen von Action und Func:

  1. Es ist schnell und schmutzig, und ich benutze es überall. Es macht Code kurz, wenn der Anwendungsfall trivial ist (benutzerdefinierte Delegaten sind mit mir aus der Mode gekommen).

  2. Noch wichtiger, der Typ ist über Domänengrenzen hinweg kompatibel. Action und Func sind Framework definiert und funktionieren nahtlos, solange die Parametertypen übereinstimmen. Sie können nicht ChangeSomeAction für ChangeListAction haben. Linq findet großen Nutzen von diesem Aspekt.

+1

Hier ist ein weiterer, wo Sie Func nicht verwenden können - Sie können Func nicht verwenden, wenn es sich wie hier erwähnt zurückgeben muss: http://stackoverflow.com/questions/27989296/how-do-i-declare-a-func-delegate- which-returns-a-func-delegate-vom-selben-type – Marwie

+0

danke @Marwie. Hilfreich. Wird zu meiner Antwort irgendwann hinzufügen. – nawfal

+0

In Beispiel # 5 muss 'params' der letzte Parameter sein. – Jalal

6

Ich habe einen speziellen Anwendungsfall zu finden, wo man nur Delegaten verwenden: 'Namespace.Class.WndEnumProc' is a 'field' but is used like a 'type':

public Func<IntPtr, IntPtr, bool> WndEnumProc; 
[DllImport("User32.dll")] 
public static extern bool EnumWindows(WndEnumProc lpEnumFunc, IntPtr lParam); 

public delegate bool WndEnumProc(IntPtr hwnd, IntPtr lParam); 
[DllImport("User32.dll")] 
public static extern bool EnumWindows(WndEnumProc lpEnumFunc, IntPtr lParam); 

Mit Func/Aktion gerade nicht funktioniert

Der folgende Code kompiliert, aber wirft s Ausnahme, wenn da System.Runtime.InteropServices.DllImportAttribute läuft nicht Serialisieren von generischen Typen unterstützen:

[DllImport("User32.dll")] 
public static extern bool EnumWindows(Func<IntPtr, IntPtr, bool> lpEnumFunc, IntPtr lParam); 

stelle ich dieses Beispiel zu jedem zeigen, dass: manchmal delegieren Ihre einzige Wahl ist. Und dies ist eine vernünftige Antwort auf Ihre Frage why not use Action<T>/Func<T> ?

0

Wie MSDN sagte Func<> selbst vordefiniert Delegate. Zum ersten Mal habe ich mich über dieses Zeug verwirrt. Nach dem Experiment war mein Verständnis klarer. Normalerweise in C#, können wir

Type als Zeiger auf Instance sehen.

Das gleiche Konzept wird auf

Delegate als Zeiger auf Method

Der Unterschied zwischen diesen, um die Dinge Delegate ist zum Beispiel nicht besitzen das Konzept der OOP, angewendet, Inheritance . Um diese Dinge klarer zu machen, ich habe die experimentellen mit

public delegate string CustomDelegate(string a); 

// Func<> is a delegate itself, BUILD-IN delegate 
//========== 
// Short Version Anonymous Function 
//---------- 
Func<string, string> fShort = delegate(string a) 
{ 
    return "ttt"; 
}; 
// Long Version Anonymous Function 
//---------- 
Func<string, string> fLong = a => "ttt"; 

MyDelegate customDlg; 
Func<string, string> fAssign; 
// if we do the thing like this we get the compilation error!! 
// because fAssign is not the same KIND as customDlg 
//fAssign = customDlg; 

Viele build-in Methoden in Rahmen (zB LINQ), erhalten die Parameter von Func<> delegieren. Das, was wir mit dieser Methode tun können, ist

Declare die Delegierten von Func<> Typ und gibt es an die Funktion eher als Define die benutzerdefinierten Delegaten.

Zum Beispiel aus dem obigen Code ich mehr Code

string[] strList = { "abc", "abcd", "abcdef" }; 
strList.Select(fAssign); // is valid 
//strList.Select(customDlg); // Compilation Error!! 
1

Für eine bessere und aufwendigere Antwort Blick auf @nawfal hinzufügen. Ich werde versuchen, einfacher zu sein.

Sie deklarieren ein Mitglied einer Klasse, also sollten Sie bei delegate bleiben. Die Verwendung von delegate ist anschaulicher und struktureller.

Action/Func Typen sind für die Weitergabe vorgesehen, so dass Sie sie mehr als Parameter und lokale Variablen verwenden sollten.

Und tatsächlich beide erben Delegate Klasse. Action und Func sind generische Typen und vereinfachen das Erstellen von Delegaten mit unterschiedlichen Parametertypen. Und das Schlüsselwort delegate erstellt tatsächlich eine ganz neue Klasse, die von Delegate in einer Deklaration erbt.