32 Stimmen

Wann soll IOrderedEnumerable zurückgegeben werden?

Debería IOrderedEnumerable als Rückgabetyp mit rein semantischem Wert verwendet werden?

Wenn wir beispielsweise ein Modell in der Präsentationsschicht verwenden, wie können wir wissen, ob die Sammlung geordnet werden muss oder bereits geordnet ist?

Was ist, wenn ein Repository eine gespeicherte Prozedur mit einer ORDER BY Klausel. Sollte das Repository Folgendes zurückgeben IOrderedEnumerable ? Und wie soll das erreicht werden?

0 Stimmen

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

27voto

Thomas Levesque Punkte 277723

Ich glaube nicht, dass das eine gute Idee wäre:

Sollte IOrderedEnumerable als Rückgabetyp rein für semantischen Wert verwendet werden?

Wenn wir beispielsweise ein Modell in der Präsentationsschicht verwenden, wie können wir wissen, ob die Sammlung geordnet werden muss oder bereits geordnet ist?

Was nützt es, zu wissen, dass eine Folge geordnet ist, wenn man nicht weiß, nach welchem Schlüssel sie geordnet ist? Der Sinn der IOrderedEnumerable Schnittstelle ist die Möglichkeit, ein sekundäres Sortierkriterium hinzuzufügen, was nicht viel Sinn macht, wenn man nicht weiß, was das primäre Kriterium ist.

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 macht keinen Sinn. Wie ich bereits sagte, IOrderedEnumerable wird verwendet, um ein sekundäres Sortierkriterium hinzuzufügen, aber wenn die Daten von der gespeicherten Prozedur zurückgegeben werden, sind sie bereits sortiert und es ist zu spät, ein sekundäres Sortierkriterium hinzuzufügen. Alles, was Sie tun können, ist, sie komplett neu zu sortieren, also rufen Sie ThenBy auf das Ergebnis hätte nicht die erwartete Wirkung.

12voto

Jon Hanna Punkte 106367

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:

  1. List<T>
  2. IList<T>
  3. ICollection<T>
  4. IEnumerable<T>
  5. IList
  6. ICollection
  7. IEnumerable
  8. 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:

  1. List<T>
  2. IList<T>
  3. ICollection<T>
  4. 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.

0voto

kofifus Punkte 14007

IMO IOrderedEnumerable und ein IOrderedCollection hätte sehr nützlich für Operationen auf hoher Ebene sein können, die z. B. auf Listen und Arrays, aber nicht auf Mengen funktionieren, aber irgendwie erbt List es nicht, so dass es diesen Zweck verloren hat. Es ist jetzt nur noch nützlich für die Art von Gedanken, die Sie im zweiten Teil Ihrer Frage gezeigt haben (Reihenfolge nach usw.)

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X