Jump to content
Unity Insider Forum
Sign in to follow this  
Carsten

C# Grundlagen - Vererbung in Unity3D

Recommended Posts

Im Rahmen meiner KI-Reihe hab ich in einem Tutorial mal das Thema Vererbung in C# etwas unter die Lupe genommen:

 

Was ist Vererbung? Wofür brauch ich das? Und vorallem: Wie mach ich das?

Diesen Fragen will in meinem 13 minütigen Video auf den Grund gehen.

 

Zum Tutorial: Unity3D Tutorial - C# Vererbung

Share this post


Link to post
Share on other sites

Das muss ich mir demnächst mal ansehen.

 

Vererbung kann in Unity leider nicht richtig ausgenutzt werden, da der Inspektor keine Werte erbender Typen zulässt - deshalb benutze ich das nie ^^

Share this post


Link to post
Share on other sites

Hallo Sascha,

 

ich weiß jetzt gerade nicht wirklich was Du mit "Werte erbender Typen" meinst.

 

Werte vererbt man ja nicht sondern nur die Eigenschaften und Methoden (und deren Variablen etc.) einer Klasse. Und das Nutzen einer Klasse bzw. eines Skripts als Basisklasse für eine andere funktioniert wunderbar, wie du dem Video entnehmen kannst.

 

Aber vielleicht missverstehe ich dich auch gerade ein bisschen ... :blink:

 

Gruß Carsten

Share this post


Link to post
Share on other sites

Ich meine folgendes:

Klasse B erbt von Klasse A

Variable ist vom Typ Klasse A und bekommt aber ein Objekt des Typs Klasse B als Wert.

 

Das will der Inspektor nicht... wäre aber supernützlich.

Share this post


Link to post
Share on other sites

Da hast Du natürlich recht, sowas geht nicht. Aber das geht auch grundsätzlich nicht, denn sonst könnte man ja auf ein Variable jedes Skript ziehen, weil ja alle Unity-Skripte von MonoBehaviour erben.

 

So ein Verhalten sollte man über Schnittstellen lösen können, wobei ich das in Unity noch nicht gemacht habe.

Share this post


Link to post
Share on other sites

Nein, aber die Skripte erben davon ... Okay, das trifft nicht ganz auf dein obiges Beispiel zu, geb ich ja zu :D

Share this post


Link to post
Share on other sites

Auch wenn das Thema schon älter ist, hätte ich auch mal eine Frage.

In einem Tutorial(1.Bild) wurde die Vererbung vorgestellt.

Nun wollte ich das mal ausprobieren(2.Bild) und habe ein funktionierendes Script um die Klasse "Superhelden" erweitert(rot eingerahmtes). Doch funktioniert das nicht und Unity gibt die Meldungen aus:

Assets/03_C#_Grundlagen/Scripts/S31ConstructorUndVererbung.cs(29,14): error CS0101: The namespace `global::' already contains a definition for `Mensch'

&

Assets/03_C#_Grundlagen/Scripts/S31ConstructorUndVererbung.cs(53,14): error CS0101: The namespace `global::' already contains a definition for `SuperMensch' .

Funktioniert das nur von einem Script zum anderen oder geht das auch innerhalb eines Scripts? Falls ja, was habe ich falsch gemacht?

vererbung2.jpg

vererbung.jpg

Share this post


Link to post
Share on other sites

Die Meldung sagt eigentlich, dass es bereits eine Klasse mit diesem Namen gibt.

Kann es sein, dass du das Script umbenannt oder verschoben hast und es diese Klassen deshalb jetzt zwei mal in deinem Projekt gibt?

Share this post


Link to post
Share on other sites

Ahja, hatte sie wirklich umbenannt und nicht gesehen das die alte noch/wieder da war.

Nun habe ich aber diese Fehlermeldungen:

Assets/03_C#_Grundlagen/Scripts/S31ConstructorUndVererbung.cs(20,29): error CS1729: The type `SuperMensch' does not contain a constructor that takes `3' arguments

Assets/03_C#_Grundlagen/Scripts/S31ConstructorUndVererbung.cs(57,12): error CS1729: The type `Mensch' does not contain a constructor that takes `0' arguments

Wenn ich das richtig verstehe, wird die Klasse nicht ausgelesen.  Man kann doch schon vorhandenes erweitern und muß nicht jedesmal alles neu schreiben, oder?

 

Share this post


Link to post
Share on other sites

Auf dem Screenshot von deinem Code, hat die Klasse Mensch einen Konstruktor, der drei Argumente erwartet.

Durch die Vererbung musst du den Konstruktor der Basisklasse bedienen. Dieser erwartet zwingend drei Argumente. Entweder du erstellst in der Basisklasse zusätzlich einen Konstruktor, der 0 Argumente akzeptiert oder du übergibst die Argumente mit dem Schlüsselwort "base" an die Basisklasse.

Vereinfacht dargestellt entweder:

    public class Mensch
    {
        public Mensch(int dasAlter, string derName, string derBeruf)
        {

        }

        public Mensch()
        {

        }
    }

    public class SuperMensch : Mensch
    {
        public SuperMensch(int dasAlter, string derName, string dieFaehigkeit)
        {

        }
    }

 

oder:

    public class Mensch
    {
        public Mensch(int dasAlter, string derName, string derBeruf)
        {

        }
    }

    public class SuperMensch : Mensch
    {
        public SuperMensch(int dasAlter, string derName, string dieFaehigkeit, string derBeruf) : base (dasAlter, derName, derBeruf)
        {

        }
    }

 

 

Share this post


Link to post
Share on other sites

Also muß ich doch nochmal alles angeben.

Im Video(1.Bild) sagt er: "die klasse Wassermonster kann alles was die Monster können, aber zusätzlich noch schwimmen..."

Deshalb bin ich davon ausgegangen  das man nicht wieder alles angeben muß.

 

So wie in deinem 1. bsp hatte ich es schon, bis auf die 2.

public Mensch()

Wozu wäre die denn gut?

 

zum 2, bsp.

Gibt man in dem Konstruktor nicht nur das an, was auch vergeben ist( der Beruf soll ja nicht angezeigt werden)?

bei mir

SuperMensch held1 = new SuperMensch(100, "Superman", "fliegen");
.
.
public SuperMensch (int dasAlter, string derName, string dieFaehigkeit)

wenn ich dann das mit Base probiere, bekomme ich auch Fehlermeldungen:

public SuperMensch (int dasAlter, string derName, string dieFaehigkeit) :base(dasAlter, derName, derBeruf)

Assets/03_C#_Grundlagen/Scripts/S31ConstructorUndVererbung.cs(57,102): error CS0103: The name `derBeruf' does not exist in the current context

wenn ich dann "derBeruf" rausnehme, kommt dies:

Assets/03_C#_Grundlagen/Scripts/S31ConstructorUndVererbung.cs(57,78): error CS1729: The type `Mensch' does not contain a constructor that takes `2' arguments

 

 

Share this post


Link to post
Share on other sites

Mit der Zeile:

 

public SuperMensch (int dasAlter, string derName, string dieFaehigkeit) :base(dasAlter, derName, derBeruf)

 

übergibst du dasAlter, derName, derBeruf an den Konstruktor der Basisklasse (durch die Verwendung von :base(dasAlter,derName,derBeruf) der in diesem Fall 3 Argumente erwartet. "derBeruf" ist aber unbekannt, da an den Konstruktor von SuperMensch die Variablen "dasAlter, derName, dieFaehigkeit" übergeben werden.

Das sagt die folgende Fehlermeldung aus:

Zitat

Assets/03_C#_Grundlagen/Scripts/S31ConstructorUndVererbung.cs(57,102): error CS0103: The name `derBeruf' does not exist in the current context

Wenn du dann wie von dir Beschrieben "derBeruf" wieder raus nimmst, werden wieder nur zwei Argumente an den Konstruktor der Basisklasse übergeben. Dadurch erhälst du die folgende Fehlermeldung:

Zitat

Assets/03_C#_Grundlagen/Scripts/S31ConstructorUndVererbung.cs(57,78): error CS1729: The type `Mensch' does not contain a constructor that takes `2' arguments

 

In dem Screenshot aus deinem Tutorial sind gar keine Konstruktoren angegeben wodurch das "Problem" nicht entsteht.

Der Konstruktor wird immer aufgerufen, wenn ein neues Objekt vom Typ dieser Klasse erstellt wird über z.B. SuperMensch test = new SuperMensch()

Es besteht die Möglichkeit für jede Klasse mehrere Konstruktoren anzugeben. Dann wird je nachdem welche Parameter übergeben werden der richtige "ausgewählt".

Wenn du nun über

public Mensch()
{

}

einen weiteren Konstruktor der Klasse Mensch hinzufügst, kann ein Objekt vom Typ Mensch entweder über

Mensch test1 = new Mensch() // ohne Argumente

oder mit

Mensch test2 = new Mensch(100, "Superman", "fliegen") // mit Argumente

erstellt werden. Da in diesem Fall, die Klasse Mensch keine Argumente zwingend erwartet (es wird ja entweder der Konstruktor ohne Argumente aufgerufen oder eben mit drei Argumenten) kann deine Klasse SuperMensch von Mensch erben ohne Argumente zu übergeben.

 

Das ganze ist auch ganz gut unter: https://docs.microsoft.com/de-de/dotnet/csharp/programming-guide/classes-and-structs/using-constructors beschrieben.

Ich hoffe ich konnte es einigermaßen verständlich beschreiben.

Share this post


Link to post
Share on other sites

Ich glaub langsam kapier ichs. Ich müßte also für jede neue Unterklasse auch einen neuen Konstruktor anlegen, wenn ich andere Werte übernehmen will.

Macht das ganze ja nicht wirklich einfacher.

 

Nun habe ich einen mit "public Mensch(int dasAlter, string derName){}" angelegt(ohne die Argumente ging es auch nicht), allerdings fehlen die Werte von der Klasse Mensch in der Ausgabe: die Fähigkeit von  ist fliegen und dessen Alter beträgt: 0

Es geht nur wenn ich wirklich alles reinschreibe  public Mensch(int dasAlter, string derName)    {  alter = dasAlter; name = derName;    }

Da sehe ich dann aber nicht, was mir die Vererbung bringen soll. Da kann ich ja gleich in der Klasse alles neu schreiben.

Share this post


Link to post
Share on other sites

Wenn Du in einer erbenden Klasse Parameter im Konstruktor übergibst, musst Du diese an die Basisklasse weiterreichen.

public class Mensch
{
  	protected readonly int alter;
  
	public Mensch(int alter)
	{
		this.alter = alter;
	}
}

public class SuperMensch : Mensch
{
	public SuperMensch(int alter) : base(alter)
	{
	}
}

Statt im Konstruktor kannst Du auch Eigenschaften beim Erzeugen vom Objekt setzen. Dies hat Vor- und Nachteile.

public class Mensch
{
  	private int alter;
  	private string name;
  
  	public int Alter
    {
      get 
      {
        return alter;
      }
      
      set
      {
        alter = value;
      }
    }
  
  	public int Name
    {
      get 
      {
        return name;
      }
      
      set
      {
        name = value;
      }
    }
}

public class SuperMensch : Mensch
{
}

//... irgendwo
var superMensch = new SuperMensch()
{
  Alter = 10,
  Name = "Hans Wurst"
};

 

Share this post


Link to post
Share on other sites
Zitat

Da sehe ich dann aber nicht, was mir die Vererbung bringen soll. Da kann ich ja gleich in der Klasse alles neu schreiben.

Die Vererbung bietet dir ja die Möglichkeit auch Methoden der Klasse von der du erbst zu nutzen.

z.B. könntest du in deiner Klasse "Mensch" Variablen für Lebenspunkte, Erfahrungspunkte usw. haben. Deine Klasse Supermensch hat dann z.B. Variablen und Methoden für Superkräfte die gewisse Dinge tun.

Deine Klasse Mensch hat aber z.B. bereits Methoden um Lebenspunkte hinzuzufügen, abzuziehen usw. Diese müsstest du dann in der Klasse Supermensch nicht noch einmal schreiben sondern könntest die Methode aus der Basisklasse verwenden.

Solltest du dann doch einmal eine Methode aus der Basisklasse "Mensch" ändern wollen in "Supermensch" kannst du diese "überschreiben" oder "erweitern".

Override - https://docs.microsoft.com/de-de/dotnet/csharp/language-reference/keywords/override

 

  • Like 1

Share this post


Link to post
Share on other sites
vor 22 Stunden schrieb amdbuster:

Da sehe ich dann aber nicht, was mir die Vererbung bringen soll. Da kann ich ja gleich in der Klasse alles neu schreiben.

Es gibt genau zwei Gründe für Vererbung: Polymorphismus, wie bereits erwähnt, und Verhinderung von Code-Duplizierung.

"Guter Code" definiert sich nicht dadurch, dass er funktioniert, sondern durch Meta-Qualitäten. Das wären so Sachen wie Lesbarkeit, Wartbarkeit, Modularität, Robustheit. Code-Duplizierung, also "alles neu schreiben", bedeutet schlechte Robustheit. Denn wenn du dann feststellst, dass dein "Mensch" doch etwas anders funktionieren soll, darfst du erstmal an beiden Stellen, an denen du definiert hast, wie ein Mensch funktioniert, deinen Code abändern. Wenn du dann noch mehr "Mensch"-Klassen hast, dann steigt dein Änderungsbedarf proportional. Wenn du dann vergisst, eine der Änderungen zu machen, hast du direkt Fehlerpotential des Todes.

Trotz alledem muss ich sagen: Ich baue selber ungefähr niemals Vererbungshierachien, wenn es um Komponenten in Unity geht. Denn Tatsache ist, dass alles, was du mit Vererbungshierarchien bauen kannst, auch mit Komponenten gebaut werden kann. Komponentenklassen, die voneinander erben, sind nie nötig und daher selten sinnvoll. Und Vorteile gibt's auch noch dazu. Von daher würde ich auf jeden Fall empfehlen zu lernen, wie Vererbung funktioniert und was man damit macht, aber in Unity für Komponenten verwenden... eher nicht.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×