Announcement

Collapse
No announcement yet.

Problem bei der Implementierung von ICollection

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Problem bei der Implementierung von ICollection

    Hallo zusammen,

    ich habe folgende Problem:
    Ich möchte gern Instanzen einer Person-Klasse in einer dynamisch erweiterbaren Liste erfassen. Da meine Liste einige spezielle Funktionen haben soll, wie beispielsweise Duplikate gleich beim Einfügen zu verhindern, habe ich dafür eine eigene Klasse erstellt:
    Code:
    public class PersonList : ICollection<Person>
    {
      private List<Person> internalList;
    
      public PersonList()
      {
        internalList = new List<Person>();
      }
    
      public void Add(Person item)
      {
        if (!Contains(item)) internalList.Add(item);
      }
    
      // weitere Methoden
    
      public IEnumerator<Person> GetEnumerator()
      {
        return internalList.GetEnumerator();
      }
    }
    Die Methoden zum Hinzufügen, Löschen, Kopieren usw. habe ich problemlos hinbekommen. Der Compiler hängt sich jetzt eigentlich nur noch am Enumerator auf. Allerdings habe ich keine Ahnung, wie ich die Methode anders implementieren soll bzw. was hier noch fehlt.

    Ich hoffe mir kann da jemand weiterhelfen.

    Vielen Dank schonmal und viele Grüße

  • #2
    Warum leitest du nicht von einer ArrayList ab und überschreist nur die Methoden die du ändern willst?
    Christian

    Comment


    • #3
      Weil ich außer der Add-Methode noch weitere Änderungen vorgenommen habe - wollte jetzt nur nicht alles posten, deshalb hab ich nur die Add-Methode als Beispiel stehen lassen. Außerdem kann ich bei einer ArrayList keine generischen Typen verwenden und müsste viel casten.
      Deshalb hab ich mich für diese Lösung entschieden. Muss ja auch irgendwie funktionieren...

      Comment


      • #4
        Generell würde ich davon abraten ICollection selbst zu implementieren. Öhm duplikate entfernen kann z.b. ein HashSet auch, wenn Du GetHashCode implementierst. Baue lieber eine Klasse um eine Liste die dann die Funktionen bereitstellt die Du auf der Liste ausführen möchtest. Sowas z.B.:

        [highlight=c#]
        public class PersonList
        {
        public IEnumerable<Personen> Persons { get; private set; }

        public PersonList()
        {
        Persons = new HashSet<Person>();
        }

        public void AddPerson(Person personToAdd)
        {
        Person.Add(personToAdd);
        }

        public IEnumerable<Person> PersonsWithName(string name)
        {
        return Persons.Where(p => p.Name == name);
        }
        }

        // alternativ gingen auch extension methods
        // das wäre dann so eine Art DSL (Domain Specific Language)
        public static class PersonExtensions
        {
        // Die Add Methode braucht man nicht implementieren da jedes HashSet sowieso eine Add Methode hat

        public static IEnumerable<Person> PersonsWithName(this IEnumerable<Person> persons, string name)
        {
        return persons.Where(p => p.Name == name);
        }
        }
        [/highlight]

        Kannst Du mir noch andere Methoden sagen die Deine Liste haben soll? Mich würde nur der Anwendungsfall interessieren

        Comment


        • #5
          "Der Compiler hängt sich an [..] auf." ist ziemlich vage ausgedrückt.

          Check doch mal, ob bei Aufruf von GetEnumerator() Deine interne Liste bereits initialisiert ist. Wenn sie noch null sein sollte, verlagere die Definition vom Konstruktor zur Deklaration.
          Zuletzt editiert von luker; 14.02.2012, 11:13.

          Comment


          • #6
            Die Methode ist korrekt nur deine Klasse ist halt unvollständig.
            Z.b. IEnumerator IEnumerable.GetEnumerator() also die nichtgenerische Version von GetEnumerator hast du nicht implementiert. Weshalb du glaubst du hättest die jetzige GetEnumerator Methode falsch implementiert dabei ist nur gemeint das eine andere GetEnumerator Methode fehlt.

            Es wäre übrigens immer hilfereich wenn du die konkrete Fehlermeldung, wenn du eine hast, auch bennenst. Sonst müssen wir hier raten und dazu haben wir (zumindest ich) nicht immmer unbedingt Lust.

            Comment


            • #7
              @fanderlf:
              Vielen Dank für dein Beispiel. Das Ganze hat allerdings einen kleinen Nachteil: Deine "Wrapper"-Klasse lässt sich nun nicht mehr als Liste oder Aufzählung identifizieren. Das heißt, bei einem Anwendungsfall, bei dem du Objekte reflektieren möchtest, hast du kaum eine Chance zu erkennen, dass hier Inhalte nicht über Properties, sondern halt über Enumeratoren geholt werden müssen.

              @Ralf:
              Mir ist nicht ganz klar, was genau noch implementiert werden muss. Vielleicht könntest du ein Beispiel für die fehlende Methode(n) posten bzw. wie ich die Klasse anpassen muss?

              Vielen Dank und viele Grüße

              Comment


              • #8
                Brauchst Du denn reflection? Wenn ich dafür reflection bräuchte dann würde ich mir ein custom attribut bauen was mir zeigt dass explizit diese property benutzt werden soll.
                Ein public Interface aus dem standard .Net Framework sozusagen als Contract für Klassen zu verwenden die per reflection gesucht werden sollen halte ich für einen fragwürdigen Ansatz.


                Ansonsten solltest Du natürlich das komplette Interface implementieren. Am Besten du löschst aus deiner Klasse erstmal alles raus was zu dem Interface gehört und lässt Dir die Funktionsrümpfe einfach nochmal neu erzeugen. Das kann Visual Studio automatisch für Dich erledigen (oder ReSharper). Dann weisst Du was Du alles implementieren musst und kannst sie einfach ausfüllen.

                Comment


                • #9
                  Was noch alles in deiner Klasse fehlt zur vollständigen ICollection<T> Implementierung findest du am besten raus in dem du einfach mal die Refactoring Fähigkeiten von Visual Studio spielen läßt (so wie fanderlf schon erwähnt hat). Dazu einfach mal das Contextmenu auf dem Interface im Editor öffnen und 'Implement Interface' ausführen. Dann hast du Stubs für alle zu implementierenden Methoden die du nur noch mit Leben füllen mußt.

                  Der übliche Weg für eine eigene Listenklasse (wenn man nicht das Verhalten volllständig ändern will) wäre eigentlich von Collection<T> abzuleiten da ist der meiste BoilerPlate Code schon erledigt und man muß(oder muß nicht wen man das Standardverhalten mag) nur noch ein paar wenige Methoden überschreiben (z.B InsertItem, RemoveItem etc.). Collection<T> entspricht standardmäßig ziemlich genau List<T> außer dem Umstand das man bei Collection<T> die entscheidenden Teile überschreiben darf was bei List<T> nicht geht.

                  Comment


                  • #10
                    Hallo,

                    Originally posted by fanderlf View Post
                    Brauchst Du denn reflection? Wenn ich dafür reflection bräuchte dann würde ich mir ein custom attribut bauen was mir zeigt dass explizit diese property benutzt werden soll.
                    also die Verwendung eines custom attributs halte ich eher für ungünstig. Wenn du nur selbst mit den Klassen arbeitest, wäre das ja nicht weiter problematisch. Aber nimm mal an, du müsstest eine Art Framework bauen, dann kannst du ja nicht jedem sagen, dass er ein spezielles Attribut implementieren soll. Da wäre es schon besser, wenn sich die Listenklasse über die von .NET standardmäßig vorgegebenen Klassen und Interfaces identifizieren lässt.

                    Originally posted by fanderlf View Post
                    Am Besten du löschst aus deiner Klasse erstmal alles raus was zu dem Interface gehört und lässt Dir die Funktionsrümpfe einfach nochmal neu erzeugen. Das kann Visual Studio automatisch für Dich erledigen (oder ReSharper).
                    Würde ich gern tun, allerdings nutze ich kein Visual Studio, sondern die kostenlose IDE Eclipse. Da habe ich jetzt allerdings keine Möglichkeit gefunden, Funktionsrümpfe neu erzeugen zu lassen, deshalb halt meine Frage...

                    Viele Grüße

                    Comment


                    • #11
                      Ich habs grad mal im Visual Studio für Dich gemacht.

                      [highlight=c#]
                      public class Test: ICollection<String>
                      {
                      public IEnumerator<string> GetEnumerator()
                      {
                      throw new NotImplementedException();
                      }

                      IEnumerator IEnumerable.GetEnumerator()
                      {
                      return GetEnumerator();
                      }

                      public void Add(string item)
                      {
                      throw new NotImplementedException();
                      }

                      public void Clear()
                      {
                      throw new NotImplementedException();
                      }

                      public bool Contains(string item)
                      {
                      throw new NotImplementedException();
                      }

                      public void CopyTo(string[] array, int arrayIndex)
                      {
                      throw new NotImplementedException();
                      }

                      public bool Remove(string item)
                      {
                      throw new NotImplementedException();
                      }

                      public int Count
                      {
                      get { throw new NotImplementedException(); }
                      }

                      public bool IsReadOnly
                      {
                      get { throw new NotImplementedException(); }
                      }
                      }
                      [/highlight]

                      String wäre halt an den entsprechenden Stelle durch Deine eigene Klasse zu ersetzen.

                      Comment


                      • #12
                        Originally posted by fanderlf View Post
                        Ich habs grad mal im Visual Studio für Dich gemacht.
                        Vielen Dank, damit hast du mir echt weitergeholfen. Hab die Implementierung jetzt hinbekommen - einwandfrei!

                        Originally posted by Ralf Jansen View Post
                        Der übliche Weg für eine eigene Listenklasse (wenn man nicht das Verhalten volllständig ändern will) wäre eigentlich von Collection<T> abzuleiten.
                        Da ich nur drei Methoden ändern will, war das auch mein erster Gedanke. Das Problem dabei ist nur, dass sich auch die Methoden von Collection<T> nicht überschreiben lassen. Hier ein Beispiel:
                        Code:
                        using System;
                        using System.Collections.ObjectModel;
                        namespace test
                        {
                          public class MyCollection : Collection<string>
                          {
                            public override void Add(string item)
                            {
                              if (!this.Contains(item)) base.Add(item);
                            }
                            public static void Main()
                            {
                              MyCollection c = new MyCollection();
                              c.Add("eins");
                              c.Add("zwei");
                              c.Add("zwei");
                              Console.WriteLine(c.Count);
                            }
                          }
                        }
                        Viele Grüße

                        Comment


                        • #13
                          Weil Add auch die falsche Methode ist zum überschreiben wenn man das Einfügeverhalten ändern will Zum Beispiel könnte man diesen Code mit dem Aufruf von Insert() anstatt Add umgehen. Collection<T> ist so geschrieben das alle Einfügeoperationen in InsertItem() landen und InsertItem ist überschreibar.
                          Das gilt Äquivalent fürs Removen, Setzen etc. die alle durch eine Methode zum überschreiben geleitet werden.

                          Sieh dir am besten mal das Beispiel(die Dinosaurs Klasse) in der Hilfe an. Dann sieht man direkt das das eigentlich recht simpel ist ohne die ganze Msdn durchackern zu müssen.

                          Comment


                          • #14
                            Vielen Dank für den Tipp.

                            Eine Frage hätte ich jetzt allerdings noch: Was passiert eigentlich genau, wenn ich statt "override" das Schlüsselwort "new" verwende, also
                            Code:
                            public class MyCollection : Collection<string>
                            {
                              public new void Add(string item)
                              {
                              }
                            }
                            Also ich meine, was läuft da genau ab. Der Sinn dieser Deklaration ist mir etwas schleierhaft.

                            Viele Grüße

                            Comment


                            • #15
                              new ~überdeckt~ die Funktion ohne das OO Konzept des Überschreibens. Es ist einfach eine Methode die zufällig den gleichen Namen trägt und beide Methoden sind weiterhin von außen erreichbar je nachdem wie man die Klasse anspricht.

                              Heißt in deinem Beispiel nur wenn du die Add Methode an einer Variablen des Typs MyCollection aufgerufen wird wird auch deine Methode aufgerufen. Wenn du über einen Basisklassen Variablen gehst oder über das ICollection Interface wird die Implementierung von Collection<string> verwendet. Da man an allen Ecken und enden nur übers Interface auf die Klasse zugreifen wird (und das .Net Framework sowie wenn man an sowas wie Databinding denkt) ist ein Methode zu überdecken eine ausgesprochen schlechte Idee und sollte möglichst nicht verwendet werden. Gehört in die selbe Ecke wie z.b goto.

                              Beispiel zum spielen (es auszuprobieren ist eh oft besser zu verstehen als eine Erklärung)

                              [Highlight=C#]class Program
                              {
                              static void Main(string[] args)
                              {
                              ClassBasis a;
                              a = new ClassBasis();
                              a.Methode(); // Ausgabe "ClassBasis.Methode1"
                              a = new ClassOverride();
                              a.Methode(); // Ausgabe "ClassOverride.Methode1"
                              a = new ClassNew();
                              a.Methode(); // Ausgabe "ClassBasis.Methode1" !!! nicht "ClassNew.Methode1" !!!!
                              ((ClassNew)a).Methode(); // Ausgabe "ClassNew.Methode1" erst ein cast führt zum Aufruf der ~new~ Methode
                              }
                              }

                              public class ClassBasis
                              {
                              public virtual void Methode()
                              {
                              Console.WriteLine("ClassBasis.Methode1");
                              }
                              }
                              public class ClassOverride : ClassBasis
                              {
                              public override void Methode()
                              {
                              Console.WriteLine("ClassOverride.Methode1");
                              }
                              }
                              public class ClassNew : ClassBasis
                              {
                              public new void Methode()
                              {
                              Console.WriteLine("ClassNew.Methode1");
                              }
                              }[/Highlight]

                              Comment

                              Working...
                              X