Wie Thomas betont, ist das Wissen, dass ein Objekt ein IOrderedEnumerable
sagt uns nur, dass es in irgendeiner Weise geordnet wurde, nicht aber, dass es in einer Weise geordnet wurde, die wir beibehalten wollen.
Es ist auch erwähnenswert, dass der Rückgabetyp Einfluss auf Überschreibungen und Kompilierbarkeit hat, aber nicht auf Laufzeitprüfungen:
private static IOrderedEnumerable<int> ReturnOrdered(){return new int[]{1,2,3}.OrderBy(x => x);}
private static IEnumerable<int> ReturnOrderUnknown(){return ReturnOrdered();}//same object, diff return type.
private static void UseEnumerable(IEnumerable<int> col){Console.WriteLine("Unordered");}
private static void UseEnumerable(IOrderedEnumerable<int> col){Console.WriteLine("Ordered");}
private static void ExamineEnumerable(IEnumerable<int> col)
{
if(col is IOrderedEnumerable<int>)
Console.WriteLine("Enumerable is ordered");
else
Console.WriteLine("Enumerable is unordered");
}
public static void Main(string[] args)
{
//Demonstrate compile-time loses info from return types
//if variable can take either:
var orderUnknown = ReturnOrderUnknown();
UseEnumerable(orderUnknown);//"Unordered";
orderUnknown = ReturnOrdered();
UseEnumerable(orderUnknown);//"Unordered"
//Demonstate this wasn't a bug in the overload selection:
UseEnumerable(ReturnOrdered());//"Ordered"'
//Demonstrate run-time will see "deeper" than the return type anyway:
ExamineEnumerable(ReturnOrderUnknown());//Enumerable is ordered.
}
Wenn Sie also einen Fall haben, in dem es entweder IEnumerable<T>
o IOrderedEnumerable<T>
an den Aufrufer zurückgegeben wird, wird die Variable je nach Umstand als IEnumerable<T>
und die Informationen aus der Rückgabeart gehen verloren. In der Zwischenzeit kann der Aufrufer unabhängig vom Rückgabetyp feststellen, ob der Typ wirklich IOrderedEnumerable<T>
.
Die Art der Rücksendung spielte dabei keine Rolle.
Bei den Rückgabetypen besteht ein Kompromiss zwischen dem Nutzen für den Aufrufer und der Flexibilität für den Aufrufenden.
Betrachten Sie eine Methode, die derzeit mit return currentResults.ToList()
. Die folgenden Rückgabearten sind möglich:
List<T>
IList<T>
ICollection<T>
IEnumerable<T>
IList
ICollection
IEnumerable
object
Schließen wir Objekt und die nicht-generischen Typen im Moment aus, da sie wahrscheinlich nicht nützlich sind (in den Fällen, in denen sie nützlich wären, sind sie wahrscheinlich unproblematisch zu verwenden). Damit bleibt übrig:
List<T>
IList<T>
ICollection<T>
IEnumerable<T>
Je weiter oben wir in der Liste stehen, desto mehr Komfort bieten wir dem Aufrufer, damit er die von diesem Typ bereitgestellten Funktionen nutzen kann, die von dem darunter liegenden Typ nicht bereitgestellt werden. Je weiter unten wir in der Liste stehen, desto mehr Flexibilität geben wir dem Aufrufer, die Implementierung in der Zukunft zu ändern. Idealerweise sollte die Liste also so weit oben stehen, wie es im Zusammenhang mit dem Zweck der Methode sinnvoll ist (um dem Aufrufer nützliche Funktionen zur Verfügung zu stellen und die Fälle zu reduzieren, in denen neue Sammlungen erstellt werden, um Funktionen anzubieten, die wir bereits anbieten), aber nicht höher (um zukünftige Änderungen zu ermöglichen).
Zurück zu unserem Fall, in dem wir eine IOrderedEnumerable<TElement>
die wir entweder als eine IOrderedEnumerable<TElement>
oder ein IEnumerable<T>
(oder IEnumerable
o object
).
Die Frage ist, ob die Tatsache, dass es sich um eine IOrderedEnumerable
inhärent mit dem Zweck der Methode verbunden, oder ist sie lediglich ein Artefakt der Implementierung?
Wenn wir eine Methode hätten ReturnProducts
die zufällig nach Preis bestellt haben, um die Fälle zu beseitigen, in denen dasselbe Produkt zweimal zu unterschiedlichen Preisen angeboten wurde, dann sollte es zurückgeben IEnumerable<Product>
denn die Anrufer sollten sich nicht darum kümmern, dass es bestellt ist, und schon gar nicht darauf angewiesen sein.
Wenn wir eine Methode hätten ReturnProductsOrderedByPrice
wenn die Bestellung Teil ihres Zwecks war, dann sollten wir zurückkehren IOrderedEnumerable<Product>
weil dies seinem Zweck näher kommt, und kann vernünftigerweise erwarten, dass der Aufruf CreateOrderedEnumerable
, ThenBy
o ThenByDescending
(das Einzige, was dies wirklich bietet), ohne dass dies durch eine spätere Änderung der Implementierung zunichte gemacht wird.
Edit: Ich habe den zweiten Teil des Textes übersehen.
Wie verhält es sich in dem Fall, dass ein Repository eine gespeicherte Prozedur mit einer ORDER BY-Klausel umschließt. Sollte das Repository IOrderedEnumerable zurückgeben? Und wie würde das erreicht werden?
Das ist durchaus eine gute Idee, wenn möglich (oder vielleicht IOrderedQueryable<T>
). Aber das ist nicht einfach.
Zunächst müssen Sie sicherstellen, dass nichts nach der ORDER BY
die Bestellung rückgängig gemacht haben könnte, ist dies möglicherweise nicht trivial.
Zweitens dürfen Sie diese Bestellung nicht durch einen Aufruf von CreateOrderedEnumerable<TKey>()
.
Zum Beispiel, wenn Elemente mit Feldern A
, B
, C
y D
werden von etwas zurückgegeben, das früher ORDER BY A DESCENDING, B
was zur Rückgabe eines Typs namens MyOrderedEnumerable<El>
die Folgendes implementiert IOrderedEnumerable<El>
. Dann ist die Tatsache, dass A
y B
sind die Felder, auf die bestellt werden muss, zu speichern. Ein Aufruf von CreateOrderedEnumerable(e => e.D, Comparer<int>.Default, false)
(das ist auch das, was ThenBy
y ThenByDescending
Aufruf in) muss Gruppen von Elementen nehmen, die gleichwertig für A
y B
nach denselben Regeln, nach denen sie von der Datenbank zurückgegeben wurden (der Abgleich von Sortierreihenfolgen zwischen Datenbanken und .NET kann schwierig sein), und nur innerhalb dieser Gruppen muss die Reihenfolge dann nach cmp.Compare(e0.D, e1.D)
.
Wenn man das tun könnte, wäre das sehr nützlich, und es wäre absolut angemessen, dass der Rückgabetyp IOrderedEnumerable
si ORDER BY
Klauseln in allen Abfragen, die bei allen Aufrufen verwendet werden, vorhanden sein.
Ansonsten, IOrderedEnumerable
wäre eine Lüge - da Sie den angebotenen Vertrag nicht erfüllen könnten - und wäre weniger als nutzlos.
0 Stimmen
Siehe dieses mögliche Duplikat stackoverflow.com/questions/829487/ または stackoverflow.com/questions/1783830/
0 Stimmen
Re: @nawfal Ich denke, Sie haben Recht, obwohl ich glaube, dass In den Leitlinien zu Metafragen heißt es, dass die anderen Fragen geschlossen werden sollen. wenn diese Frage besser diskutiert werden kann. Sie hat sicherlich mehr aber Ihr erstes mögliches Duplikat hat eine gute Skeet-Antwort und der zweite ist die Antwort ist kurz und bündig . Sie haben mich also erwischt.
;^D