2 Stimmen

Wie konvertiert man eine generische Sammlung in eine generische Sammlung von Vorfahren?

Betrachten Sie eine Liste von Label:

Collection labels = new Collection();

Jetzt möchte ich dies in eine Sammlung von Controls umwandeln:

public void ScaleControls(ICollection controls) {}

Ich würde versuchen zu rufen:

ScaleControls(labels);

aber das lässt sich nicht kompilieren.

ScaleControls((ICollection)labels);

kompiliert, aber stürzt zur Laufzeit ab.

ICollection controls = (ICollection)labels;
ScaleControls(c);

kompiliert, aber stürzt zur Laufzeit ab.

Gibt es eine Möglichkeit, generische Listen von Objekten zu übergeben?


Die Alternative besteht darin, generische Listen zu verwerfen und typisierte Listen zu verwenden:

public class ControlList : Collection
{
}

public void InvalidateControls(ControlList controls)
{
}

Aber das bedeutet, dass der gesamte Code, der Generika verwendet, nachgerüstet werden muss.

0 Stimmen

Welche Version von .Net verwenden Sie?

0 Stimmen

Muss die Sammlung ursprünglich vom Typ Label sein oder könnte man auch mit Control davonkommen, da Label von Control erbt?

3 Stimmen

Du solltest dich mit Kovarianz und Kontravarianz beschäftigen, das könnte dein Problem lösen. Leider verwenden nur wenige der eingebauten Kollektionen dies.

10voto

phoog Punkte 40628

Sie können es nicht umwandeln; Sie müssen es selbst konvertieren.

InvalidateControls(new List(labels));  //C# 4 oder später

Ihr Problem hier ist, dass ICollection nicht kovariant ist. Der Grund, warum ICollection nicht kovariant ist, liegt darin, Methoden wie diese zu verhindern:

void AddControl(ICollection controls, Control control)
{
    controls.Add(control);
}

Warum wäre das böse? Weil wenn ICollection kovariant wäre, würde die Methode es Ihnen ermöglichen, TextBoxes zu Listen von Labeln hinzuzufügen:

AddControl(new List(), new TextBox());

List hat einen Konstruktor, der IEnumerable nimmt. Warum können wir labels übergeben? Weil IEnumerable kovariant ist: Sie können nichts in ein IEnumerable einfügen; Sie können nur Dinge herausnehmen. Aufgrund dessen wissen Sie, dass Sie alles, was aus dem IEnumerable abgerufen wird, als ein T behandeln können (natürlich) oder eines seiner Basistypen.

BEARBEITEN

Ich habe gerade bemerkt, dass Sie .Net 3.5 verwenden. In diesem Fall ist IEnumerable nicht kovariant, und Sie benötigen mehr Code, um die Sammlung zu konvertieren. Etwas wie dies:

ICollection ConvertCollection(ICollection collection) where U : T
{
    var result = new List(collection.Count);
    foreach (var item in collection)
        result.Add(item);
}

0 Stimmen

Cast gibt ein IEnumerable zurück, anstatt der ICollection, die in diesem hypothetischen Beispiel erforderlich ist.

0 Stimmen

Ich sehe nicht, warum das kein lösbares Problem ist: controls.Add(new HttpWebRequst()); ArgumentException - kann Typ HttpWebRequest nicht zu ICollection hinzufügen

0 Stimmen

@IanBoyd ja, ich habe das bei IEnumerable bemerkt und korrigiert.

4voto

Eine schnelle Antwort auf die Frage:

labels.Cast().ToList()

Dies erstellt eine ganz neue, separate Liste, so dass, wenn Sie die neue Sammlung an eine Methode übergeben, die eine Steuerelement hinzufügt, wird dieses neue Steuerelement nicht in der Originalsammlung labels widergespiegelt.

Eine Alternative besteht darin, die Methode zu überprüfen, der Sie die Sammlung übergeben. Angenommen, Sie haben eine Methode wie folgt:

    void AddControl(List controls, string controlName)
    {
        Control ctrl = this.FindControlByName(controlName);

        controls.Add(ctrl);
    }

Sie können kein List Objekt an diese Methode übergeben, aber Sie können es tun, wenn Sie sie als generische Methode umschreiben, wie unten gezeigt:

    void AddControl(List controls, string controlName)
        where T : Control
    {
        Control ctrl = this.FindControlByName(controlName);

        controls.Add((T)ctrl); // eine Umwandlung ist erforderlich
    }

Zugegeben, die obigen Vorschläge sind möglicherweise nicht möglich oder bevorzugt, je nach Ihrer Situation.

Wie in Ihrer eigenen Antwort angegeben, eine weitere Möglichkeit besteht darin, die nicht-generischen Schnittstellen zu nutzen. Dies ist ein völlig gültiger Ansatz; Ich denke, dass wir seit dem Erscheinen von Generika in .NET 2.0 im Allgemeinen scheu vor Umwandlungen geworden sind, weil wir denken, dass sie irgendwie "schlecht" sind, aber manchmal sind Umwandlungen einfach notwendig, wenn es um Polymorphismus geht.

1voto

Ian Boyd Punkte 232380

Die Lösung, die ich gefunden habe - aufhören, Collections zu verwenden:

List labels = new List();

und dann wird die Methode zu:

public void ScaleControls(IList controls) {}

Ich erhalte den Vorteil von generischen Listen, ohne dass .NET sich darüber beschwert, dass es das Offensichtliche nicht tun kann.

Und wenn jemand folgendes aufruft:

controls.Add(new System.Xml.XmlDocument());

dann erhalte ich den Fehler:

ArgumentException
Der Wert "System.Xml.XmlDocument" ist nicht vom Typ "System.Windows.Forms.Label" und kann nicht in dieser generischen Sammlung verwendet werden.

Genau wie ich es erwarten würde.

3 Stimmen

Gutes Beispiel für das XY-Problem perlmonks.org/index.pl?node_id=542341

0 Stimmen

@L.B. Ich weiß, ich weiß. Meine Frage war "Wie kann ich zu einem Vorfahren umwandeln?" Die Antwort, die ich akzeptieren muss, lautet "du kannst es nicht". Ich könnte eine separate Frage stellen: "Wie kann ich das tun, was ich möchte, obwohl es nicht möglich ist?" - aber es besteht kein Bedarf, eine völlig neue Frage zu öffnen, wenn die Antwort hier ist.

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