Zum Inhalt springen

Mehr als ein Objekt einer Klasse erstellen?


 Teilen

Empfohlene Beiträge

Hallo zusammen,

da mir in diesem Forum beim letzten mal so gut geholfen wurde, wende ich mich mit einer Frage zur oop nochmal an euch. Mein Testprojekt zur oop soll eine Art Spiel werden, wobei das Spiel ansich nicht erstellt werden soll, sondern nur die Idee der Character Erschaffung. Es soll möglich sein mehrer Character aus verschiedenen Rassen und verschiedenen Klassen zu erstellen. Welche dann (wenn ich mich mit dem Speichern von Datein befasst habe), auch abgespeichert und wieder geladen werden können.

In einem Online Rollenspiel ist es zum Beispiel möglich mehrere Character zu erstellen, vielleicht sogar mit verschiedenen Rassen und eventuell sogar diese mit Unterschiedlichen Klassen. Dazu kommen auch noch andere Spieler, welche genauso wie Spieler1 Character aus verschiedenen Rassen mit verscheidenen Klassen erstellen können.

namespace Klassenprojekt2
{
    class Program
    {
        static void Main(string[] args)
        {
            PlayerCharacter playerCharacter = new PlayerCharacter();

            Console.Write("Bitte den Charakternamen eingeben: ");
            playerCharacter.name = Console.ReadLine();

            Console.Write("Bitte das Alter deines Charakters eingeben: ");
            playerCharacter.age = int.Parse(Console.ReadLine());

            Console.WriteLine(playerCharacter.name + " " + playerCharacter.age);

            Console.ReadKey();
        }
    }

    class PlayerCharacter
    {
        public string name;
        public int age;
    }
}

Nach meinem Verständnis erstelle ich in meinem Beispiel allerdings nie ein Neues Objekt, außer beim ersten mal. Jedes mal, wenn ich ein Objekt playerCharacter erstelle, wird das vorherige playerCharacter Objekt ersetzt. Im Internet habe ich gelesen, dass man viele verschiedene Objekte durch die Erstellung einer <List> oder eben einem Arrey erzeugen kann. Wobei in meinem Fall ein Arrey nicht praktikabel wäre, da es ja begrenzt wäre. Meine Frage besteht jetzt darin, ob es der richtige Gedanke wäre, nun das Objekt folgendermaßen zu erzeugen.

    class Program
    {
        static void Main(string[] args)
        {
            List<PlayerCharacter> playerCharacter = new List<PlayerCharacter>();
        }
    }

Mein erster Versuch endete darin das mir eine Fehlermeldung Ausgegeben wurde. Leider habe ich diesen Versuch natürlich nicht gespeichert und kann diese jetzt nicht vorzeigen.

Freundliche Grüße Gewinde 🙂

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hi, Objekte erstellt man immer mit dem new Operator (Den benutzt du aber nur 1x PlayerCharacter playerCharacter = new PlayerCharacter(); ). Wenn man den benutzt wird der Konstruktor der Klasse aufgerufen von der du das Objekt erstellst. Der fehlt bei dir. Solltest du dich mal drüber informieren.

Du müsstest einfach nur mehrere Objekte erstellen -  anschließend kannst du die in Arrays oder Lister packen wie du lustig bist. Das dient aber eher zur Handhabung verschiedener Objete - nicht der Erstellung.  

 

Bearbeitet von bene98
Link zu diesem Kommentar
Auf anderen Seiten teilen

Richtig. Du kannst folgendes schreiben:

var players = new Player[]
{
    new PlayerCharacter(),
    new PlayerCharacter(),
    new PlayerCharacter()
};

Und schon hättest du ein Array mit 3 Spielern.

Mit dem new-Operator wird ein Objekt im Arbeitsspeicher (genaugenommen im sog. Heap) angelegt. Jedes Objekt hat somit seinen eigenen Speicherbereich und ist deswegen unabhängig von den anderen Objekten. Wenn wir also mit players[0] und players[1] auf die Objekte zugreifen wollen, haben wir unterschiedliche Objekte vom selben Typ.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Meine beiden Vorredner haben sehr viel bereits ausgeführt, aber was bedeutet das denn nun konrekt für Dein Codebeispiel?

Habe an dieser Stelle einfach einmal versucht, das geschriebene Wissen in Deinem Code umzusetzen, Kommentare einzufügen und die entsprechenden Stellen möglichst einfach und hoffentlich einigermaßen verständlich zu erklären:

using System;

// Damit die Typ List erkannt wird, ist es nötig, deren Namespace dem Compiler bekannt zu machen
using System.Collections.Generic;

namespace Klassenprojekt2
{
    public class Program
    {
        public static void Main(string[] args)
        {
			var players = new List<PlayerCharacter>
			{
				// Achtung: Diese Variante des Erzeugen und Ausfüllen der Objekte funktioniert nur deshalb, weil in der Klasse PlayerCharacter die beiden Eigenschaften als public gekennzeichnet wurden!
				//new PlayerCharacter { name="aaa", age=111 },
				//new PlayerCharacter { name="bbb", age=222 },
				//new PlayerCharacter { name="ccc", age=333 },
			};

			var playerCharacter = new PlayerCharacter();
			
            Console.WriteLine("Bitte den Charakternamen eingeben: ");
            playerCharacter.name = Console.ReadLine();

            Console.WriteLine("Bitte das Alter deines Charakters eingeben: ");
            playerCharacter.age = int.Parse(Console.ReadLine());

			players.Add(playerCharacter);
			
			// Anmerkung: Hier einfach mal googlen wie man die Methode ToString() überschreiben kann
			Console.WriteLine(players[0].name + " " + players[0].age);

			Console.ReadLine();
        }
    }

    class PlayerCharacter
    {
        public string name;
        public int age;
    }
} 

Ein paar Anmerkungen zu den Änderungen:

Es wurde direkt eine Liste für die Speicherung der Objekte verwendet, weil zuvor schon festgestellt worden ist, dass ein Array eine feste Länge hat und erst umständlich vergrößert werden müsste.

@bene98: Es ist hier übrigens nicht erforderlich einen Standardkonstruktor (leeren Konstruktor) in der Klasse PlayerCharacter zu definieren, da dieser beim Kompilieren durch den Compiler unter der Haube hinzugefügt wird.

Ausblick für die weiteren notwendigen Änderungen und das weitere Lernen:

Die Kommentare sollen bereits ein paar Anregungen für weitere mögliche Lernthemen gegeben haben.

Aber an dieser Stelle sollte auch klar geworden sein, dass für das Hinzufügen einer variablen Anzalh von PlayerCharacter-Objekten und entsprechender Abfrage deren Daten vom Benutzer, wohl noch andere Sprachkonstrukte von Nöten sind (Lern-Stichpunkt: Schleifen/Loops).

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hi, ich weiß das schon . Ich denke, es wäre nur sinnvoller, wenn der Threadersteller ein paar Sachen bei der Geschichte lernen würde. Ist doch eigentlich ein schönes Projekt, was er/sie da hat. Man kann da ne menge über Klassen, Objekte, Methoden Zugriffsmodifikatoren, Container, Vererbung, Polymorphie etc bei lernen. Nur man sollte die Person vielleicht dazu bringen sich mit den Themen zu beschäftigen und es dann testweise umzusetzen anstatt es schon hinzuschreiben.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hallo zusammen,

ich habe mir mal einige Gedanken gemacht und erstmal mit der Basisklasse begonnen und etwas mit dem code gespielt. Leider kann ich den gesamten code nicht anhängen, daher mal wieder als schriftliche Ausgabe:

namespace TerraProjekt
{
    enum Breed { Human , Psionic , Experimental}
    enum Gender { neuter , Male , Female}

    class MainPopulation
    {
        private int strength;
        public int Strength { get { return strength; } set => strength = value; }

        private int Stamina { get; set; }

        public MainPopulation (int creatureStrength, int creatureStamina)
        {
            Strength = creatureStrength;
            Stamina = creatureStamina;
        }

        public void WertAusgabe()
        {
            Console.WriteLine("Deine Werte sind: {0} {1}" , strength , Stamina);
        }
    }
}

Ausführung in der Main Methode:

        static void Main(string[] args)
        {
            MainPopulation player = new MainPopulation(50, 100);

            player.WertAusgabe();

            Console.ReadKey();
        }

Die enums im Namespace einfach nicht beachten, die sind für später vorgehalten. Als nächstes versuche ich eine Vererbung in die erste Unterklasse und danach dann als weiteres Experiment die Problematik mehrerer Objekte dieser Klasse. Da ich nicht für jeden Quark ein neues Thema aufmachen möchte, werde ich hier alle Schritte mit eventuellen Fragen u.s.w. reinwerfen und daraus mein ProjektThema machen. Ich hoffe das ist i.O. für euch. Ich würde mich darüber freuen wenn ihr einfach mal drüber schaut und mir ggf. verbesserungsvorschläge geben könntet. Getter und Setter habe ich zwei verschiedene Varianten zum üben benutzt, eigentlich sollte ein Automatischer ja ausreichen. Meine Frage hierzu wäre, wenn man eine Variable direkt über den Konstruktor anspricht (this.strength = creatureStrength). Kann dann der Getter und Setter gänzlich weggelassen werden, da die Zuweisung ja über das Schlüsselwort this erfolgt?

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hallo zusammen,

ich habe die Tage etwas an meinem Projekt getüfftelt und dabei sind natürlich mal wieder einige Fragen aufgetreten. Die eine Frage dreht sich um das besagte Arrey oder die besagte Liste. Erstmal wollte ich diesbezüglich nochmal nachfragen ob ich bene98 Antwort richtig verstehe. Da ich mittels Konstruktor ohnehin immer eine neues ( anders) Objekt als das vorherige erzeuge, ist ein Arrey/List zur erstellung vieler verschiedener Character gar nicht nötig?

Ich möchte dieses Problem (auch wenn gar nicht erforderlich) gerne weiter verfolgen, einfach um dadurch zu lernen und zu verstehen. Ich habe z.B. ein Arrey erstellt welches zur Probe genau 1 index hat (index 0), in dieses habe ich ein Objekt gespeichert und habe mir zur überprüfung das Objekt mittels for-Schleife ausgeben lassen.

            PlayerCharacter[] player = new PlayerCharacter[1];
            player[0] = new PlayerCharacter();

            PlayerCharacter population = new PlayerCharacter();

           // einzelne Methodenaufrufe....

            for(int i = 0; i < player.Length; i++)
            {
                Console.WriteLine(player[i]);
            }

 

Das Ergebnis ProjektTerra.PlayerCharacter finde ich etwas wenig aussagekräftig und daher bin ich mir jetzt nicht wirklich sicher ob der Versuch auch funktioniert hat. Bedeutet diese Ausgabe das die Speicherung erfolgt ist?

 

Bearbeitet von Gewinde
Unfähigkeit im Umgang mit der Tastatur...
Link zu diesem Kommentar
Auf anderen Seiten teilen

Vielleicht hilft es Dir beim Verständnis, wenn Du Dir die angehängten Bilder ansiehst, welche eine SEHR vereinfachte Darstellung des Arbeitsspeicher eines Computers zeigen (in allen Abbildungen durch die Tabelle repräsentiert):

Variablen im Speicher:

vars.png

Wenn man Variablen erzeugt

// Deklaration
int i;

// Deklaration und Initialisierung
int i = 12345;

werden deren Lebensräume (Speicherbereiche) durch das Betriebssystem möglichst effizent zur Verfügung gestellt, so dass spätere Zugriffe möglichst schnell erfolgen können. So belegt z.B. eine Variable vom Typ int 4 Bytes und lebt, ähnlich wie Menschen an einer bestimmten Adresse. Hierbei ist anzumerken, dass Adresse sich immer auf den Anfang des jeweiligen Speicherbereichs bezieht und die Größe des Typs die Länge des belegten Speichers angibt.

Klassen im Speicher:

vars_class.png

Eine Klasse ist einfach nur ein zusammenhängender Speicherblock, welcher alle Klassenvariablen der Reihe nach enthält. Natürlich ist das in Wahrheit erheblich komplexer, wie @Whiz-zarD weiter oben bereits angedeutet hatte. Zum anfänglichen Verständnis sollte dies dennoch ausreichend sein. Für die Abbildung habe ich Deine PlayerCharacter-Klasse von weiter oben verwendet und das xxx (x) soll einfach nur weitere Membervariablen und deren Wert symbolisieren.

Arrays im Speicher:

array_class.png

Hier habe ich versucht ein Array des Typs PlayerCharacter im Speicher darzustellen. Die []-Klammern stellen dabei den Index für den Zugriff auf die jeweiligen Arrayelemente dar. Die restlichen Bestandteile wurden im vorherigen Punkt ausgeführt.

Bearbeitet von el_pollo_diablo
Link zu diesem Kommentar
Auf anderen Seiten teilen

Und nach diesem Erklärungsversuch zu Deinem eigentlichen Post:

vor 7 Stunden schrieb Gewinde:

Da ich mittels Konstruktor ohnehin immer eine neues ( anders) Objekt als das vorherige erzeuge, ist ein Arrey/List zur erstellung vieler verschiedener Character gar nicht nötig?

Da hast Du @bene98 leider falsch verstanden. Jede Deklaration einer Variablen reserviert einen Speicherbereich in der passenden Größe (abhängig vom Variablentyp). Was er meinte ist, dass ein Array, bzw. eine Liste zum einfachen Zugriff auf vielen Variablen des gleichen Typs verwendet wird. Oder anders gesagt, irgendwie musst Du dem Computer ja mitteilen, auf welche Variablen Du zugreifen möchtest... Ohne einen "Container" würde man für jede Variable einen eigenen Variablennamen angeben müssen und bei vielen hundert Variablen mal zig Zugriffen ist das dann doch "etwas" umständlich.

vor 7 Stunden schrieb Gewinde:

            PlayerCharacter[] player = new PlayerCharacter[1];

            player[0] = new PlayerCharacter();

            PlayerCharacter population = new PlayerCharacter();

Diese drei Zeilen würden vereinfacht so dargestellt werden:

array_and_var.png

Der lila Bereich entspräche Deinem player-Array vom Typ PlayerCharacter mit einem einzigen Element. Der gelbe Bereich der Variablen population vom Typ PlayerCharacter.

Hier zeigt sich bereits, dass ein simples Handling (z.B. für das die Ausgabe der beiden eigenständigen Objekte via Console.WriteLine()) zwei getrennte Befehle benötigen würde:

Console.WriteLine(player[0]);
Console.WriteLine(population);

Viel bequemer geht dies dann natürlich unter Verwendung eines Arrays und über eine Schleife, z.B. die for-Variante, welche von Dir bereits gespostet worden ist.

Ich hoffe nun wenigstens etwas Licht ins Dunkel des Speicherkellers gebracht zu haben 😉

Bearbeitet von el_pollo_diablo
Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich würde an deiner Stelle erst mal noch ein bisschen an der Klasse playerCharacter arbeiten. Hast du mal verschiedene Charakter angelegt und die in eine array List gepackt? Hast du eine to String Methode für die Ausgabe der Eigenschaften des Characters. Kannst du über die Liste iterieren und die Eigenschaften aller Charaktere ausgeben. Haben die Charaktere vielleicht Waffen? Dann könntest du eine klasse Weapon schreiben und einem Charakter eine Waffe zuordnen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 17 Stunden schrieb Gewinde:

Das Ergebnis ProjektTerra.PlayerCharacter finde ich etwas wenig aussagekräftig und daher bin ich mir jetzt nicht wirklich sicher ob der Versuch auch funktioniert hat. Bedeutet diese Ausgabe das die Speicherung erfolgt ist?

Natürlich ist sie erfolgreich, denn das Programm hat dir nichts gegenteiliges mitgeteilt. ;)

Hätte es nicht geklappt, hättest du ein "null" auf der Konsole ausgewiesen bekommen. Mit dem new-Operator befehligst du dem Gargabe Collector eine Instanz deiner Klasse zu erzeugen. D.h. der Gargabe Collector reserviert für dich den Speicher und gibt diesen auch wieder frei, wenn es keine Referenzen mehr auf dieses Objekt gibt. Die Referenz des Objektes speicherst du dann in dein Array.

Um etwas weiter auszuholen, wie die Speicherverwaltung funktioniert:

Es gibt erstmal zwei Arten von Datentypen: Wertetypen und Referenztypen.
Wertetypen sind z.B. int, double, decimal, float, ... Sie haben eine definierte Größe.
Referenztypen sind unsere Objekte. Sie haben eine variable größe. Je nachdem wie wir die Klassen schreiben.

Dann gibt es zwei Arten von Speicherbereichen: Stack und Heap.

Der Stack ist der Bereich, wo deine lokalen Variablen, je nach Zuständigkeitsbereich, abgelegt werden. Beispiel:

public void Methode()
{
    // i ist in der gesamten Methode sichtbar
    // da sie direkt im Methoden-Block deklariert wurde
    int i = 0; 
    
    for(int x = 0; x < 100; x++)
    {
        int j = 0;
        // x und j sind nur im Block der for-Schleife sichtbar
    }
}

Wenn man Methode() aufruft, dann wird für die Methode die Variable i auf den Stack gespeichert. Für die for-Schleife werden die Variablen x und j gespeichert. Innerhalb der Schleife haben wir Zugriff auf i, da die Schleife innerhalb des Zustänidgkeitsbereich der Methode liegt. Ist die Schleife dann durch, dann können die Variablen die innerhalb der for-Schleife deklariert worden sind, wieder entfernt werden (x und j), weil wir sie nicht mehr brauchen und alles, was außerhalb der Schleife ist, kann nicht darauf zugreifen.

Der Name "Stack" kommt daher, dass die Variablen immer auf einen Stapel gelegt werden. Dies ist möglich, weil die Größe einer Variable immer bekannt ist. Auch von Objekten, obwohl ich doch schrieb, dass sie unterschiedlich groß sind. Wieso das so ist, sehen wir gleich.

Der Heap ist für die Objekte zuständig. Wenn wir also ein Objekt mittels dem new-Operator erzeugen, ermittelt der Garbage Collector, wie groß ein Objekt ist und sucht im Heap einen geeigneten Speicherbereich und gibt uns eine sog. Referenz zurück. Die Referenz nicht der Wert des Objektes, sondern eine Speicheradresse. Also die Adresse im Speicher, wo auch unser Objekt liegt. Die Adresse ist je nach 32- oder 64-Bit-Anwendung auch 32- oder 64-Bit lang und somit schließt sich auch der Kreis um den Stack. Wenn wir also nun:

var player = new PlayerCharacter();

aufrufen, dann wird im Stack eine Variable mit dem Namen player gespeichert, die als Wert eine Speicheradresse besitzt.

Etwas ähnliches passiert auch mit deinem Array. Genaugenommen speicherst du also nicht die Objekte in einem Array, sondern lediglich nur die Referenzen und durch die geschickte Syntax ist dann eine sog. explizite Deferenzierung, wie unter C/C++ nicht nötig, denn die Deferenzierung wurde mit Hilfe der sog. Punkt-Notation gelöst. Wenn du jetzt schreibst:

Console.WriteLine(playerArray[0].Name);

Wird mit dem Punkt (.) hinter playerArray[0] die Adresse dereferenziert. D.h. wir haben dann Zugriff auf die Eigenschaften/Methoden des Objektes. Mit der Dereferenzierung teilen wir also mit, dass wir den Inhalt des Speicherbereichs haben wollen.

Ich hoffe, dass es erstmal so verständlich.

 

Dass die Konsole "ProjektTerra.PlayerCharacter" ausgibt ist auch richtig. Die Methode WriteLine() ruft von deinem Objekt wiederrum die ToString()-Methode auf. Diese Methode steht jeder Klasse zur Verfügung und die Standard-Implementierung ist halt, dass der vollqualifizierte Name der Klasse zurückgegeben wird. Mit Hilfe des Schlüsselwortes override kannst du aber diese auch überschreiben:

public class PlayerCharacter
{
    public override string ToString()
    {
        // Deine Implementierung
    }
}
Bearbeitet von Whiz-zarD
Link zu diesem Kommentar
Auf anderen Seiten teilen

Deine Meinung

Schreibe jetzt und erstelle anschließend ein Benutzerkonto. Wenn Du ein Benutzerkonto hast, melde Dich bitte an, um unter Deinem Benutzernamen zu schreiben.

Gast
Auf dieses Thema antworten...

×   Du hast formatierten Text eingefügt.   Formatierung wiederherstellen

  Nur 75 Emojis sind erlaubt.

×   Dein Link wurde automatisch eingebettet.   Einbetten rückgängig machen und als Link darstellen

×   Dein vorheriger Inhalt wurde wiederhergestellt.   Editor leeren

×   Du kannst Bilder nicht direkt einfügen. Lade Bilder hoch oder lade sie von einer URL.

 Teilen

Fachinformatiker.de, 2021 by SE Internet Services

fidelogo_small.png

Schicke uns eine Nachricht!

Fachinformatiker.de ist die größte IT-Community
rund um Ausbildung, Job, Weiterbildung für IT-Fachkräfte.

Fachinformatiker.de App

Download on the App Store
Get it on Google Play

Kontakt

Hier werben?
Oder sende eine E-Mail an

Social media u. feeds

Jobboard für Fachinformatiker und IT-Fachkräfte

×
×
  • Neu erstellen...

Wichtige Information

Fachinformatiker.de verwendet Cookies. Mehr dazu in unserer Datenschutzerklärung