none
Fehler im Code von "System.Collections.IList(Of)" RRS feed

  • Frage

  • Hi,

    es gibt ja von IList, ICollection, IEnumerable auch jeweils die generische Version (IList(Of), IColelction(Of), ...). Diese sollten sich in ihrer Logik eigentlich ja nicht unterscheiden.

    Allerdings implemtiert IList eine "Function Add(value as Object) as Integer". Soweit ok, auch wenn Controls die mit Collections arbeiten eher das hinzugefügt Objekt selbst, als nur seinen Index zurückgeben (vgl. ListView-Control (System.Windows.Forms.ListView.ListViewItemCollection.Add(item as ListViewItem) as ListViewItem).

    IList(Of) implementiert aber "Sub Add(item as T)", was doch schon ein wenig merkwürdig ist.

    Eigentlich ist es auch egal, da es (meiner Meninung nach) ausreicht das hinzugefügte Objekt zurückzugeben wenn der Aufrufer es nicht direkt übergeben hat (Also etwa bei "Sub Add(name as String, count as Integer)" oder so), aber das ist natürlich ein wenig inkonsequent. 

    Hat jemand eine Idee, was sich die Entwickler dabei gedacht haben, oder haben sie eben das nicht gemacht?

    Danke 

    Momo

     

     

    Montag, 29. November 2010 06:46

Alle Antworten

  • Hallo Momo

    Bei den Überladungen von ListViewItemCollection.Add
    http://msdn.microsoft.com/en-us/library/system.windows.forms.listview.listviewitemcollection.add.aspx

    geht es primär um das Erstellen von neuen Item-Instanzen, der eine Fall
    für bereits existierenden Item wurde halt in selber Signatur gehalten.


    Beachte, mit den Generics ergeben sich zusätzliche/andere Möglichkeiten, wurden aber erst mit .NET 1.1 eingebaut,
    somit sind diverse Unterschiede (architektonisch, absichtlich und auch in der Systematik) naheliegend.
    Es konnte AFAIK nie das Ziel sein, die zwei Collection-Stämme absolut identisch zu machen.
      (und die Collections/Generics/Forms-Collections stammen auch aus 3 verschiedenen MS-Teams)


    zu deinem
     >> IList(Of) implementiert aber "Sub Add(item as T)", ... merkwürdig ist.

    hast du dazu den exakten MSDN-Link? ich denke du sprichst mal vom Interface mal von der Klasse.

     


    Montag, 29. November 2010 08:28
  • Hallo Momo,

    eine Antwort dazu findest Du bei Brad Abrams:

    Why does IEnumerable<T> inherits from IEnumerable

    Gruß Elmar

    Montag, 29. November 2010 09:24
    Beantworter
  • Hallo Momo,

    Entscheidend ist das Thema:

    [Kovarianz und Kontravarianz in Generika] [Info2]
    http://msdn.microsoft.com/de-de/library/dd799517.aspx

    Zwar ist Anders Heilsberg Artikel von 2005 schon eine gute Erklärung, aber er wurde vor 5 Jahren gepostet und seitdem hat sich ein "wenig" geändert. Insofern ... frei von mir ins Deutsche übersetzt mit ein paar Anmerkungen für .NET 4.0:

    • Im Idealfall würden alle der generischen Collection-Interfaces (z.B. ICollection<T> IList<T>) von ihren nicht-generischen Gegenstücken erben, so dass generische Interface-Instanzen mit generischem und nicht generischen Code zusammen genutzt werden könnten. Zum Beispiel wäre es praktisch, wenn eine IList<T> an Code übergeben werden könnte, der eine IList erwartet.
       
      Wie sich herausstellt, ist die einzige generische Schnittstelle, für die dies möglich ist:
      IEnumerable<T>, denn nur IEnumerable<T> ist kontravariant: [**Hinweis von mir: in .NET 4.0 kovariant!**]. Bei IEnumerable<T> wird der Typparameter T nur in "Rückgabe"-Positionen verwendet wird (Rückgabewerte) und nicht bei "Eingabe"-Positionen (Parametern). ICollection<T> und IList<T> nutzen T in Eingabe- und Ausgabe-Positionen [**Hinweis von mir: s. out Modifizierer in .NET 4.0**], und diese Schnittstellen sind daher invariant. (Nebenbei - sie wären kontravariant gewesen, wenn T nur in Eingabe-Positionen [**Hinweis von mir: s. in Modifizierer in .NET 4.0**] verwendet würde, aber das ist hier nicht wirklich relevant.)
       
      Die Wirkung, wenn eine nicht-kontravariante generische Schnittstelle von ihrem nicht-generischen Gegenstück erbt, wird am besten mit einem Beispiel illustriert. Stellen Sie sich vor: IList<T> erbt von IList. IList<T> hätte dann zwei Add-Methoden - die eigene Add(T)-Methode und eine vererbte Add(Object)-Methode. Zwar ist dies technisch möglich, aber dann würde die geerbten Add(Object)-Methode komplett dem Zweck der IList<T>widersprechen, weil es mit einem Objekt eines beliebigen Typs aufgerufen werden kann. Mit anderen Worten, ich würde schreiben können:
       
            IList<int> zahlen = GetIntList(...);
            zahlen.Add("Hallo");
       
      und der Aufruf von "Add" würde IList<int>'s Implementation von Add(Objekt) aufrufen, die vermutlich eine Ausnahme zur Laufzeit werfen würde. Die starke Typisierung von IList<T> wäre verloren, und es gäbe wenig Grund, IList<T> an erster Stelle zu haben. Aus diesem Grund erbt IList<T> nicht von IList. Natürlich empfehlen wir, dass Collections beides implementieren, aber dann entstünde der Verlust von Typsicherheit, weil ich dann explizit die IList-Implementierung erhalten müßte:
       
           IList<int> zahlen = GetIntList(...);
           IList objekte = (IList)zahlen;
           objekte.Add("Hallo");
       
      Die Geschichte ist anders bei IEnumerable<T>. Es hat eine Methode,
      GetEnumerator(), die einen IEnumerator<T> zurückgibt, was wiederum eine Current-Eigenschaft des Typs T hat. In allen Fällen tritt T in einer Ausgabe-Position auf, und starke Typisierung wird nicht verhindert, wenn das Methoden-Rückgabe-Objekt zugefügt wird. Intuitiv gesehen ist es "sicher", T als Objekt zu behandeln, aber nicht umgekehrt.
       
      Also, um die Frage zu beantworten, IEnumerable<T> erbt von IEnumerable, weil IEnumerable<T> es kann! :-)


    ciao Frank

    Montag, 29. November 2010 20:47
  • Danke für eure Mühen.

    @Thomas: 

    Meinst du den Link (http://msdn.microsoft.com/de-de/library/63ywd54z.aspx), oder hab ich deine Frage missverstanden? Um ehrlich zu sein rede ich durchgängig von den Interfaces - vom ListView mal abgesehen.

    Eigentlich kann ich mir nicht vorstellen, dass diese Differenzen zwischen den beiden Interfaces auf Grund mangelnder Absprache oder Kenntnis des anderen Interfaces sind. Immerhin ist das ja schon .NET 4.0 und hätte schon öfters angepasst werden können.

    @Elmar + Frank:

    Ich will ja nicht nerven :'-( , aber so ganz beantwortet das meine Frage nicht; der Autor hat wohl die gleiche Vorstellung wie ich - die Klassen sollten sich nur in der Typensicherheit uinterscheiden...

    Mal ein bisschen weg vom Thema der Frage zum Artikel von Adams: Könnte IList(Of T) nicht von IList erben und dann die "doppelten" Member als shadows kennzeichnen? Macht im Prinzip keinen Sinn, weil der Nutzer der Klasse nicht mehr durchblickt, aber rein theoretisch...?

     

    Danke

    Momo

    Montag, 29. November 2010 23:36
  • Eigentlich kann ich mir nicht vorstellen, dass diese Differenzen zwischen den beiden Interfaces auf Grund mangelnder Absprache oder Kenntnis des anderen Interfaces sind. Immerhin ist das ja schon .NET 4.0 und hätte schon öfters angepasst werden können.

    Momo,
    nicht unbedingt mangelnde Absprache, sondern andere/neue Vorstellungen/Ziele (MS Teams haben enorme Freiheiten).

    Die Architektur von derart grundlegenden Interfaces ändert man nicht einfach so von Version zu Version, dies ergäbe fast immer irgendwo breaking-changes, was potentiell allen existierenden managed Code der Welt gefährdet.
      (am Framework wurde IMHO sonst schon [zu-]viel geschraubt)
    Design auf dieser elementaren Ebene macht man also entweder gleich zu Beginn korrekt, oder lebt auf ewig mit den Mängeln.
    Dienstag, 30. November 2010 00:11
  • Hallo Momo,

    man hat bei den generischen Auflistungs-Schnittstellen ein Redesign vorgenommen,
    als sie für .NET 2.0 und für Generika überarbeitet wurden.

    Zunächst aus meinem Gedächtnis:
    Vornehmliches Ziel war, die Schnittstellen leichtgewichtiger zu gestalten.
    Die ursprünglichen Schnittstellen (IEnumerable, ICollection, IList, IDictionary) orientierten
    sich  an den Konzepten wie man sie in Java findet (dort noch überladener, siehe z. B. Collection )

    Mit der gewonnenen Praxis in .NET 1.X hatte sich herausgestellt, dass einige Dinge eher Ballast waren -
    was damals mehr aufstiess, da man noch mehr Listen selbst kodierte (z. B. wegen des Boxing)
    und Generatoren wie Codesmith deswegen in Mode kamen.
    Heute findet das praktisch deutlich weniger statt, den Genrika sei Dank ;-)
    Siehe dazu z. B.: Collection<T> versus List<T> what should you use on your interfaces?

    Hätte man direkte Vererbung gewählt, so hätten die generischen Schnittstellen
    dies auf Dauer mitgeschleppt - und man müsste es selbst auch implementieren.
    Durch die Trennung kann man auf die nichtgenerischen  Schnittstellen verzichten
    (und z. B. IList weglassen, wenn man es nicht benötigt).

    Auch konnte man Probleme, wie das schon zitierte mit Add(object item) vs. Add(T item) vermeiden.

    Andere wurde mit etwas Trickserei umgangen. So implementiert System.Array (siehe Wichtig)
    die generischen Schnittstellen zur Laufzeit, d. h. sie tauchen nicht direkt auf - sonst wäre
    wohl das eine oder andere  .NET 1.x Programm bei einer Migration auf die Nase gefallen.

    Die Rückgabe des Index für IList.Add ist ohnehin unglücklich, denn es verführte bei Überladungen
    zu einem "fröhlichen Mischmasch" von Rückgaben, wie auch bei ListViewItem.Add (und anderen
    in Windows Forms) -  und ich habe noch schlimmeres gesehen.
    Da ist es konsequenter, auf einen Rückgabewert zu verzichten; und man muß
    nicht die Überladungsliste durchforsten, was man verwenden kann.

    Einige der Aussagen der damaligen Autoritäten, soweit ich sie in Kürze
    noch finden konnte - in den MSDN Blogs findet man um 2004/5 weiteres:
    .NET Arrays, IList<T>, Generic Algorithms, and what about STL? [Brian Grunkemeyer]

    Generic interfaces, IsReadOnly, IsFixedSize, and array
    System.Collections vs. System.Collection.Generic and System.Collections.ObjectModel
    (die Übersetzung überlasse ich denn mal anderen)

    Gruß Elmar

    Dienstag, 30. November 2010 01:06
    Beantworter
  • Hallo Momo,

         > der Autor hat wohl die gleiche Vorstellung wie ich - die Klassen
         > sollten sich nur in der Typensicherheit uinterscheiden...

    die Intention ist genauso, er erklärt aber auch, aus welchen Gründen es nicht geht
    Auch im Redesign von .NET 4.0 bleiben diese Argumente erhalten.
    Hier weitere "Meinungen" dazu:

    [Why doesn't ICollection<T> implement ICollection? - Stack Overflow]
    http://stackoverflow.com/questions/2353346/why-doesnt-icollectiont-implement-icollection


    ciao Frank
    Dienstag, 30. November 2010 06:33