Laden...

Objekte identifizieren

Erstellt von sra vor 18 Jahren Letzter Beitrag vor 18 Jahren 7.470 Views
S
sra Themenstarter:in
230 Beiträge seit 2004
vor 18 Jahren
Objekte identifizieren

Hallo

Ich bin gerade dabei ein kleines Spiel zu programmieren. Nun habe ich einen Timer gemacht, der bei jedem Tick für alle Objekte, die sich bewegen können (IMoveable) in einer ArrayList (Objects) die Methode move() aufzurufen, und danach die refresh() Methode des Fensters (ist bloss ein kleines Projekt und ich arbeite mit GDI+).

Mein Problem ist nun das Identifizieren von Objekten. Ich will eigentlich alle Objekte durchlaufen,welche das Interface IMoveable implementieren. Dazu habe ich folgenden Code:

foreach(IMoveable Object in this.m_Level.Objects)
			{
				Object.move();
			}

Aber irgendwie kommt er nie in zu dem Object.move(), da er gar nie in die Schleife fällt.

Dann hab ich mit der Klasse direkt (Turkey) und einer for Schleife probiert versucht:

for(int i = 0; i < this.m_Level.Objects.Count; i++)
			{
				if(this.m_Level.Objects[i].GetType().ToString() == "demGrenyBattle.Game.Objects.Turkey")
				{
					((Turkey)this.m_Level.Objects[i]).move();
				}

				this.m_Window.Refresh();
			}

Auch das funktioniert nicht, weil er kein einziges mal in die if Anweisung springt. Gebe ich aber über eine MessageBox in der for Schleife den Typen des jeweiligen Objektes aus, dann gibt er mir durchaus auch "demGrenyBattle.Game.Objects.Turkey" aus.

Hat jemand eine Idee? Bin wirklich seit ner halben Stunde am probieren.

Wenn Zeit in Geschichte übergeht und keine Blüten trägt werden Zukunftsbilder blass //Clueso

6.862 Beiträge seit 2003
vor 18 Jahren

Den Typennamen als String zu vergleichen ist murks. Du kannst die Typen direkt vergleichen.

Ansich kann bei deiner foreach Schleife nicht viel schiefgehen. Implementieren die Klassen wirklich IMovable? Ist die Collection auch nicht leer?

Du meintest er kommt erst gar nicht in die Schleife, meinst du damit es passt kein Objekt auf IMovable oder das er die Schleife nicht erreicht? Dann müsste in dem Code vorher was schiefgehen.

Baka wa shinanakya naoranai.

Mein XING Profil.

S
sra Themenstarter:in
230 Beiträge seit 2004
vor 18 Jahren

Wie kann ich denn rausfinden, ob ein Objekt von einem bestimmten Typen ist (ohne es mit einem anderen Objekt zu vergleichen)? Habe nur die Stringlösung gefunden 😠

Zum anderen: Die ArrayList ist nicht leer - es sind alle Objekte vorhanden. In die Schleife komme ich nur im ersten Codebeispiel nicht, da dort die Abfrage ja bereits in der Schleife drin ist (foreach). Im zweiten Beispiel mit der for Schleife komme ich rein, und mache ich direkt in der for-Schleife ein MessageBox.show(), der mir den Typen des jeweiligen Objektes ausgibt, dann kommt wie gesagt dieser Typ vom der Turkeyklasse. Durch die if Abfrage schafft es das Objekt trotzdem nicht.

btw. Ist es auch möglich abzufragen in einer if Anweisung, ob ein Objekt ein bestimmtes Interface implementiert?

Wenn Zeit in Geschichte übergeht und keine Blüten trägt werden Zukunftsbilder blass //Clueso

M
1.439 Beiträge seit 2005
vor 18 Jahren
Hinweis von herbivore vor 10 Jahren

Link korrigiert

49.485 Beiträge seit 2005
vor 18 Jahren

Hallo sra,

ja, das geht mit dem is-Operator. Aber Vorsicht: explizite Typ-Abfragen sind ein Indiz für schlechtes Design.

herbivore

S
sra Themenstarter:in
230 Beiträge seit 2004
vor 18 Jahren

Danke - ich werds mal probieren. Allerdings denke ich nicht, dass es damit geht, da es ja in der foreach auch nicht geht. Hat jemand eine Idee, warum meine foreach nicht geht? Ihr könnt davon ausgehen, dass Objects gefüllt ist mit mehreren Objekten, wovon mindestens 3 das Interface IMoveable implementieren und vom Typ Turkey sind.

Ich hab echt keine Idee mehr

Wenn Zeit in Geschichte übergeht und keine Blüten trägt werden Zukunftsbilder blass //Clueso

49.485 Beiträge seit 2005
vor 18 Jahren

Hallo sra,

ich denke, dass er erst gar nicht zu dem foreach kommt. Denn käme er dort hin und würden in this.m_Level.Objects Objekte sein, die IMoveable nicht implementieren, würdest du eine Exception bekommen.

foreach(IMoveable Object in this.m_Level.Objects) heißt, dass alle Objekte in this.m_Level.Objects IMoveable implementieren müssen und nicht etwas, dass das foreach nur für die Objekte ausgeführt wird, die IMoveable implementieren.

herbivore

S
sra Themenstarter:in
230 Beiträge seit 2004
vor 18 Jahren

hm... dann habe ich foreach nicht richtig verstanden? 😠

Dann mach ich es mal ganz banal. Ich habe in einer ArrayList einige Objekte, die das Interface implementieren, und einige die dies nicht tun. Der Unterschied besteht in erster Linie darin, dass IMoveable Objekte eine Methode move() haben, welche ihre Koordinaten manipuliert. Nun hab ich einen Timer, welcher alle x ms bei allen beweglichen Objekten diese Methode aufrufen soll, damit diese ihre neue Position berechnen und danach darstellen.

Komischerweise geht es mit dem is Operator in der for schleife. Allerdings bin ich auch der Meinung, dass dies nicht wirklich eine schöne Lösung ist.

Wenn Zeit in Geschichte übergeht und keine Blüten trägt werden Zukunftsbilder blass //Clueso

49.485 Beiträge seit 2005
vor 18 Jahren

Hallo sra,

du könntest dir einen Enumerator schreiben, der nur die Objekte, die IMovable implementieren, aufzählt. Ein Beispiel für einen (etwas anderen) Enumerator findest du in foreach iteration gleichzeitig über mehrere arrays . Ist aber für einen Anfänger bestimmt eine Herausforderung.

herbivore

S
sra Themenstarter:in
230 Beiträge seit 2004
vor 18 Jahren

Is schon traurig, was man als "Programmierlehrling" bis ins 4te Lehrjahr gelehrt bekommt 🙂

Ich werd mich mal an deinen Code wagen, obwohl er für mich wirklich eine Nummer zu gross aussieht (nur schon die Rekursion). Aber im Moment quält mich noch eine andere, mit Sicherheit recht einfache Frage.

Im onPaint des frmMain hab ich den Code zum Darstellen meiner Objekte (dieses mal alle und nicht nur die mit dem Interface 😄). Mein Problem ist, dass ich von einem anderen objekt aus dieses onPaint Ereignis auslösen muss. Ich dachte das geht ganz einfach, wenn ich dem Objekt eine Referenz auf die frmMain mitgebe, und dann vom Objekt aus .Refresh() auslöse. Leider ist dem nicht so (kein Kompilierfehler, keine Exception).

Wie kann ich das bewerkstelligen?

Wenn Zeit in Geschichte übergeht und keine Blüten trägt werden Zukunftsbilder blass //Clueso

49.485 Beiträge seit 2005
vor 18 Jahren

Hallo sra,

warum musst du das von einem anderen Objekt aus machen? Ich denke, dass wäre nicht notwendig. Im Gegenteil: Ein Objekt sollte in sich abgeschlossen sein. Versuche das Programm so umzustellen, dass frmMain sich selbst Refreshed.

herbivore

S
sra Themenstarter:in
230 Beiträge seit 2004
vor 18 Jahren

Das muss so sein, weil ich eine Klasse habe, die den ganzen Gameloop managed. Das heisst in ihr ist der Timer, der die Objekte in seinem Event bewegt, in ihr wird der Level instantiert (und in diesem wiederum die Levelobjects...).

Ich denke es ist nur sauber getrennt, wenn ich den ganzen Code nicht im Code des Fensters habe. Oder was meint ihr?

Wenn Zeit in Geschichte übergeht und keine Blüten trägt werden Zukunftsbilder blass //Clueso

68 Beiträge seit 2005
vor 18 Jahren

Original von sra
Is schon traurig, was man als "Programmierlehrling" bis ins 4te Lehrjahr gelehrt bekommt 🙂

Traurig wenn man sich in seiner Bildung auf andere verlaesst und nicht aktiv selbst lernt...

Sinnvoller Ansatz waere auch einfach nur eine "DoSomething" Methode einzufuehren, so durchlaeufst du alle deine Objekte, startest DoSomething, und alle Objekte "bewegen" sich selbst

49.485 Beiträge seit 2005
vor 18 Jahren

Hallo sra,

Ich denke es ist nur sauber getrennt, wenn ich den ganzen Code nicht im Code des Fensters habe. Oder was meint ihr?

das, was du da abgetrennt hast, ist der Controler im MVC-Pattern. Die Überlegung ist grundsätzlich sinnvoll. Da der Controler aber in der Regel ohnehin von der Anwendungsart abhängt (Konsole, WindowsForms, Web, ...), würde ich ihn nicht vom (ebenfalls anwendungsartabhängigen) View (also hier dem Form) trennen.

Wenn du es aber doch trennst, sollte der Controler das View kennen (bzw. sogar erzeugen) und kann dann auch problemlos Refresh aufrufen.

herbivore

PS:

Traurig wenn man sich in seiner Bildung auf andere verlaesst und nicht aktiv selbst lernt...

Also ich habe gerade in diesem Thread den Eindruck, dass sra sich um das aktiv Selbstlernen bemüht.

68 Beiträge seit 2005
vor 18 Jahren

Offtopic:

Original von herbivore
Also ich habe gerade in diesem Thread den Eindruck, dass sra sich um das aktiv Selbstlernen bemüht.

Mag sein, wobei die Aussage sich anhoerte wie "Ich verlange das mir alles beigebracht wird, ich aber nichts selbst erarbeiten will"

49.485 Beiträge seit 2005
vor 18 Jahren

Hallo UschkinRedSunshine,

für mich nicht. Dazu kenne ich einfach genug gute Leute, die aktiv selber lernen, und trotzdem bzw. gerade deshalb bedauern, dass die Qualität der (berufs-)schulischen Ausbildung schlecht ist.

herbivore

S
sra Themenstarter:in
230 Beiträge seit 2004
vor 18 Jahren

Ich werd mir das mal überlegen Herbivore - danke... meld mich wohl morgen mit dem nächsten Problem 😁

Zu dem lernen-Ding kann ich sagen, dass ich leider im Geschäft bisher nur mit vb und asp zu tun hatte, und dass ich in der Schule speziell im objektorientierten Programmieren sehr gute Noten habe. Dazu kommt, dass wir noch ein Semester haben bis die Lehre fertig ist (3 Lektionen programmieren die Woche), und wir in ca. 2 Wochen mal mit Designpatterns anfangen werden. Von da her finde ich das schon schlecht.

Wenn Zeit in Geschichte übergeht und keine Blüten trägt werden Zukunftsbilder blass //Clueso

49.485 Beiträge seit 2005
vor 18 Jahren

Hallo sra,

hier kommt die Klasse MovableEnumerator, mit der du genau das tun kannst, was du willst.

EDIT: Weiter unten gibt es eine Implementierung, bei der der gewünschte Elementtyp als Typparameter übergeben werden kann.


foreach (IMovable mov in new MovableEnumerator (al)) {
   mov.Move ();
}


using System;
using System.Collections;

public class MovableEnumerator : IEnumerator, IEnumerable
{
   private IEnumerator    _ienum;

   public MovableEnumerator (ICollection icoll)
   {
      _ienum = icoll.GetEnumerator ();
   }

   public Object Current
   {
      get { return _ienum.Current; }
   }

   public bool MoveNext ()
   {
      while (_ienum.MoveNext ()) {
         if (_ienum.Current is IMovable) {
            return true;
         }
      }
      return false;
   }

   public void Reset ()
   {
      _ienum.Reset ();
   }

   public IEnumerator GetEnumerator ()
   {
      return this;
   }
}

interface IMovable
{
   void Move ();
}

public class Movable : IMovable
{
   public void Move ()
   {
   }
}

public class NonMovable
{
}

abstract class App
{
   public static void Main (string [] astrArg)
   {
      ArrayList al = new ArrayList ();
      al.Add (new Movable ());
      al.Add (new NonMovable ());
      al.Add (new Movable ());
      al.Add (new NonMovable ());
      al.Add (new Movable ());

      foreach (IMovable mov in new MovableEnumerator (al)) {
         Console.WriteLine (mov.GetType ());
      }
   }
}

Im Gegensatz zu foreach iteration gleichzeitig über mehrere arrays habe ich auf zwei geschachtelte Klassen verzichtet und nur eine Enumerator-Klasse geschrieben und diese Klasse mit einem Trick (return this) foreach-geeignet gemacht.

herbivore

S
sra Themenstarter:in
230 Beiträge seit 2004
vor 18 Jahren

yeah - Vielen Dank 🙂
werds mir gleich anschauen!

Wenn Zeit in Geschichte übergeht und keine Blüten trägt werden Zukunftsbilder blass //Clueso

49.485 Beiträge seit 2005
vor 18 Jahren

Hallo zusammen,

eine überarbeitete Version des MovableEnumerator findet ihr als Iter.Filter [EDIT]und später auch als Iter.Type[/EDIT] in Hilfreiche Iteratoren / Improving Foreach.

herbivore

49.485 Beiträge seit 2005
vor 18 Jahren

Hallo zusammen,

der MovableEnumerator funktionierte ja nur für IMovable. Hier kommt ein Iterator für beliebige Typen. Er listet nur die Elemente der Aufzählung auf, die vom angegebenen Typ sind. Für IMovable sieht der Aufruf so aus:


foreach (IMovable mov in new IterType<IMovable> (al)) {
   mov.Move ();
}


public class IterType<T> : IEnumerator<T>, IEnumerable<T> where T : class
{
   //-----------------------------------------------------------------------
   protected IEnumerator  _enumerator;

   //=======================================================================
   public IterType (IEnumerable enumerable)
   {
      _enumerator = enumerable.GetEnumerator ();
      //Reset ();
   }

   //=======================================================================
   public void Reset ()
   {
      _enumerator.Reset ();
   }

   //=======================================================================
   public bool MoveNext ()
   {
      while (_enumerator.MoveNext ()) {
         if (_enumerator.Current is T) {
            return true;
         }
      }
      return false;
   }

   //=======================================================================
   public T Current
   {
      get { return (T)_enumerator.Current; }
   }

   //=======================================================================
   Object IEnumerator.Current
   {
      get { return _enumerator.Current; }
   }

   //=======================================================================
   public void Dispose ()
   {
      // Nichts zu tun
   }

   //=======================================================================
   public IEnumerator<T> GetEnumerator ()
   {
      return this;
   }

   //=======================================================================
   IEnumerator IEnumerable.GetEnumerator ()
   {
      return this;
   }
}

herbivore