Warum Func anstelle von Predicate ?
Frage
Das ist nur eine Kuriosität Frage, die ich frage mich, ob jemand eine gute Antwort auf hatte:
In der .NET Framework-Klassenbibliothek haben wir zum Beispiel diese beiden Methoden:
public static IQueryable<TSource> Where<TSource>(
this IQueryable<TSource> source,
Expression<Func<TSource, bool>> predicate
)
public static IEnumerable<TSource> Where<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate
)
Warum verwenden sie Func<TSource, bool>
statt Predicate<TSource>
? Scheint, wie die Predicate<TSource>
nur durch List<T>
und Array<T>
verwendet wird, während Func<TSource, bool>
durch so ziemlich alle Queryable
und Enumerable
Methoden und Erweiterungsmethoden verwendet wird ... was damit los ist?
Lösung
Während Predicate
hat zur gleichen Zeit eingeführt worden, die List<T>
und Array<T>
, in .net 2.0, die verschiedenen Func
und Action
Varianten kommen aus .net 3.5.
So jene Func
Prädikate verwendet werden, vor allem für die Konsistenz in den LINQ-Operatoren. Ab .net 3.5, über Func<T>
mit und Action<T>
die Richtlinie Staaten :
Sie die neue LINQ Arten
Func<>
verwenden undExpression<>
statt benutzerdefinierten Delegierten und Prädikate
Andere Tipps
Ich habe dies vor sich. Ich mag die Predicate<T>
Delegierten - es ist schön und beschreibend. Allerdings müssen Sie die Überlastung von Where
berücksichtigen:
Where<T>(IEnumerable<T>, Func<T, bool>)
Where<T>(IEnumerable<T>, Func<T, int, bool>)
Das erlaubt Ihnen auch auf dem Index des Eintrags zu filtern. Das ist schön und konsequent, während:
Where<T>(IEnumerable<T>, Predicate<T>)
Where<T>(IEnumerable<T>, Func<T, int, bool>)
wäre nicht.
Denn der eigentliche Grund für Func
anstelle eines bestimmten Delegierten ist, dass C # behandelt als völlig unterschiedliche Arten getrennt Delegierten erklärt.
Auch wenn Func<int, bool>
und beide Predicate<int>
identisch Argument haben und Rückgabetypen, sie sind nicht zuweisungskompatibel. Also, wenn jede Bibliothek einen eigenen Delegattyp für jeden Delegierten Muster erklärt, würden diese Bibliotheken nicht in der Lage sein, zu interagieren, wenn der Benutzer Einsätze „Überbrückung“ Delegierten Umwandlungen durchzuführen.
// declare two delegate types, completely identical but different names:
public delegate void ExceptionHandler1(Exception x);
public delegate void ExceptionHandler2(Exception x);
// a method that is compatible with either of them:
public static void MyExceptionHandler(Exception x)
{
Console.WriteLine(x.Message);
}
static void Main(string[] args)
{
// can assign any method having the right pattern
ExceptionHandler1 x1 = MyExceptionHandler;
// and yet cannot assign a delegate with identical declaration!
ExceptionHandler2 x2 = x1; // error at compile time
}
Mit jeder Förderung Func zu verwenden, Microsoft hofft, dass dies das Problem inkompatibler Delegattypen wird lindern. Jeder Teilnehmer wird gut zusammen spielen, weil sie nur nach oben angepasst werden auf der Grundlage ihrer Parameter / Rückgabetypen.
Es löst nicht alle Probleme, weil Func
(und Action
) nicht out
oder ref
Parameter haben, aber die sind weniger häufig verwendet.
Update: in den Kommentaren Svish sagt:
Dennoch Schalten eines Parametertyp aus Func auszusagen und zurück, scheint nicht zu machen Unterschied? Zumindest kompiliert es noch ohne Probleme.
Ja, solange Ihr Programm nur Methoden an den Delegierten zuweist, wie in der ersten Zeile meiner Main
Funktion. Der Compiler erzeugt geräuschlos Code zu neuen einem Delegatobjekt, die auf das Verfahren leitet auf. Also in meiner Main
Funktion, kann ich x1
ändern Typ ExceptionHandler2
sein, ohne ein Problem zu verursachen.
Doch in der zweiten Zeile ich versuche, den ersten Delegierten zu einem anderen Delegierten zuweisen. Selbst angenommen, dass die 2. Delegattyp genau den gleichen Parameter hat und Rückgabetypen, der Compiler gibt Fehler CS0029: Cannot implicitly convert type 'ExceptionHandler1' to 'ExceptionHandler2'
.
Vielleicht wird es deutlicher:
public static bool IsNegative(int x)
{
return x < 0;
}
static void Main(string[] args)
{
Predicate<int> p = IsNegative;
Func<int, bool> f = IsNegative;
p = f; // Not allowed
}
Meine Methode IsNegative
ist eine ganz gute Sache auf die p
und f
Variablen zugewiesen werden, solange es so direkt zu tun. Aber dann kann ich nicht zuordnen eine dieser Variablen auf der anderen Seite.
Der Rat (in 3.5 und höher) ist die Action<...>
und Func<...>
zu verwenden - „Warum?“ Für die - ein Vorteil ist, dass „Predicate<T>
“ nur dann sinnvoll ist, wenn Sie wissen, was „Prädikat“ bedeutet -. Andernfalls müssen Sie bei Objekt-Browser aussehen (usw.), um die signatute zu finden
Im Gegensatz Func<T,bool>
folgt ein Standardmuster; Ich kann sofort sagen, dass dies eine Funktion, die eine T
nimmt und gibt ein bool
- brauchen keine Terminologie zu verstehen -. Nur meine Wahrheit Test anwenden
„Prädikat“ dies OK gewesen sein könnte, aber ich schätze den Versuch zu standardisieren. Es ermöglicht auch eine Menge Parität mit den entsprechenden Methoden in diesem Bereich.