Ich habe eine IEnumerable<T>
Methode, mit der ich Steuerelemente auf einer WebForms-Seite finde.
Die Methode ist rekursiv und ich habe einige Probleme, den gewünschten Typ zurückzugeben, wenn yield return
der Wert des rekursiven Aufrufs zurückgegeben wird.
Mein Code sieht wie folgt aus:
public static IEnumerable<Control>
GetDeepControlsByType<T>(this Control control)
{
foreach(Control c in control.Controls)
{
if (c is T)
{
yield return c;
}
if(c.Controls.Count > 0)
{
yield return c.GetDeepControlsByType<T>();
}
}
}
Dies löst derzeit den Fehler "Ausdruckstyp kann nicht konvertiert werden" aus. Wenn diese Methode IEnumerable<Object>
jedoch den Typ zurückgibt , wird der Code erstellt, aber der falsche Typ wird in der Ausgabe zurückgegeben.
Gibt es eine Möglichkeit yield return
, Rekursion zu verwenden?
c#
generics
ienumerable
yield
Jamie Dixon
quelle
quelle
if(c.Controls.Count > 0)
->if(c.Controls.Any())
, besonders wenn Sie auch nachgeben :)yield
. Bitte siehe unten :) Und es ist auch einyield return
bei rekursiven Funktionen zu vermeiden , dass die Speichernutzung explosionsartig skaliert. Siehe stackoverflow.com/a/30300257/284795Antworten:
Innerhalb einer Methode, die zurückgibt
IEnumerable<T>
,yield return
muss zurückgegeben werdenT
, nicht eineIEnumerable<T>
.Ersetzen
mit:
quelle
Sie müssen jedes der Elemente zurückgeben, die durch den rekursiven Aufruf erhalten werden:
Beachten Sie, dass das Rekursieren auf diese Weise mit Kosten verbunden ist. Am Ende werden viele Iteratoren erstellt, die zu Leistungsproblemen führen können, wenn Sie über einen wirklich tiefen Kontrollbaum verfügen. Wenn Sie dies vermeiden möchten, müssen Sie die Rekursion innerhalb der Methode grundsätzlich selbst durchführen, um sicherzustellen, dass nur ein Iterator (Zustandsmaschine) erstellt wird. In dieser Frage finden Sie weitere Details und eine Beispielimplementierung. Dies führt jedoch natürlich auch zu einer gewissen Komplexität.
quelle
c.Controls.Count > 0
vs..Any()
:)Wie Jon Skeet und Colonel Panic in ihren Antworten festhalten, kann die Verwendung
yield return
rekursiver Methoden zu Leistungsproblemen führen, wenn der Baum sehr tief ist.Hier ist eine generische nicht rekursive Erweiterungsmethode, die eine Tiefenüberquerung einer Folge von Bäumen durchführt:
Im Gegensatz zur Lösung von Eric Lippert arbeitet RecursiveSelect direkt mit Enumeratoren, sodass Reverse nicht aufgerufen werden muss (wodurch die gesamte Sequenz im Speicher gepuffert wird).
Mit RecursiveSelect kann die ursprüngliche Methode des OP einfach wie folgt umgeschrieben werden:
quelle
Andere haben Ihnen die richtige Antwort gegeben, aber ich glaube nicht, dass Ihr Fall vom Nachgeben profitiert.
Hier ist ein Ausschnitt, der dasselbe erreicht, ohne nachzugeben.
quelle
yield
auch? ;)foreach
Schleife hat mich immer gestört . Jetzt kann ich das mit rein funktionaler Programmierung machen!Sie müssen die Elemente in Ihrem zweiten vom Enumerator zurückgeben, nicht vom Enumerator selbst
yield return
quelle
Ich denke, Sie müssen jedes der Steuerelemente in den Aufzählungszeichen zurückgeben.
quelle
Die Syntax von Seredynski ist korrekt, aber Sie sollten darauf achten,
yield return
rekursive Funktionen zu vermeiden, da dies eine Katastrophe für die Speichernutzung darstellt. Siehe https://stackoverflow.com/a/3970171/284795. Es skaliert explosionsartig mit der Tiefe (eine ähnliche Funktion verbrauchte 10% des Speichers in meiner App).Eine einfache Lösung besteht darin, eine Liste zu verwenden und sie mit der Rekursion https://codereview.stackexchange.com/a/5651/754 zu übergeben
Alternativ können Sie einen Stapel und eine while-Schleife verwenden, um rekursive Aufrufe https://codereview.stackexchange.com/a/5661/754 zu vermeiden
quelle
Obwohl es viele gute Antworten gibt, möchte ich dennoch hinzufügen, dass es möglich ist, LINQ-Methoden zu verwenden, um dasselbe zu erreichen.
Zum Beispiel könnte der ursprüngliche Code des OP wie folgt umgeschrieben werden:
quelle
OfType
ist nicht wirklich ein meainingful anders. Höchstens eine geringfügige styalistische Veränderung. Ein Steuerelement kann nicht mehreren Steuerelementen untergeordnet sein , daher ist der durchquerte Baum bereits eindeutig. Die Verwendung vonUnion
anstelle vonConcat
ist unnötig, um die Eindeutigkeit einer Sequenz zu überprüfen, die bereits als eindeutig garantiert ist, und ist daher eine objektive Herabstufung.