Zum Inhalt
wannacode

static: was alle Objekte teilen

Kurz gesagt: Ein statisches Feld oder eine statische Methode gehört zur Klasse, nicht zum Objekt: Alle Objekte sehen einen gemeinsamen Wert, zum Beispiel einen Zähler der erzeugten Objekte.

Problem

Jedes Produkt ist Nr. 1

Der Shop will, dass jedes neue Produkt die nächste Nummer bekommt: 1, 2, 3… Du legst einen Zähler nextId in die Klasse. Der Konstruktor gibt dem Produkt die aktuelle Nummer und erhöht den Zähler:

class Product
{
private int nextId = 1;
private int id;
private string name;
public Product(string name)
{
this.name = name;
id = nextId;
nextId++;
}
public override string ToString()
{
return "Nr. " + id + " " + name;
}
}
Product bread = new Product("Brot");
Product milk = new Product("Milch");
Product coffee = new Product("Kaffee");
Console.WriteLine(bread);
Console.WriteLine(milk);
Console.WriteLine(coffee);
Ausgabe
Nr. 1 Brot
Nr. 1 Milch
Nr. 1 Kaffee

Alle sind Nr. 1. Der Grund ist, dass nextId ein Feld des Objekts ist, genau wie der Name. Jedes neue Produkt bekommt seinen eigenen Zähler, der bei 1 beginnt. Der Zähler des Brotes kommt nie bei der Milch an.

Die Idee

Ein Feld der Klasse, nicht des Objekts

Der Zähler gehört nicht zu einem Produkt. Alle Produkte teilen ihn sich. Füge static hinzu:

private static int nextId = 1;

Ein static-Feld ist ein Feld der Klasse. Es gibt eines, egal wie viele Objekte du erzeugst, und alle Objekte teilen es sich:

Program.cs
class Product
{
private static int nextId = 1;
private int id;
private string name;
public Product(string name)
{
this.name = name;
id = nextId;
nextId++;
}
public override string ToString()
{
return "Nr. " + id + " " + name;
}
}
class Program
{
static void Main()
{
Product bread = new Product("Brot");
Product milk = new Product("Milch");
Product coffee = new Product("Kaffee");
Console.WriteLine(bread);
Console.WriteLine(milk);
Console.WriteLine(coffee);
}
}
Ausgabe
Nr. 1 Brot
Nr. 2 Milch
Nr. 3 Kaffee

Das Brot nimmt die 1 und macht den Zähler zu 2. Die Milch sieht denselben Zähler, nimmt die 2 und macht ihn zu 3.

private static int nextId = 1;
public Product(string name)
{
this.name = name;
id = nextId;
nextId++;
}
Product bread = new Product("Brot");
Product milk = new Product("Milch");
Product coffee = new Product("Kaffee");
Schritt für Schritt, in Worten
  1. nextId ist static: ein Feld in der Klasse, nicht eines in jedem Objekt.
  2. new Product("Brot"): ein neues Objekt. Der Konstruktor speichert den Namen.
  3. id = nextId: Das Objekt nimmt die aktuelle Nummer, 1.
  4. nextId++ ändert den gemeinsamen Zähler: Jetzt ist er 2.
  5. Milch: derselbe Zähler. Es nimmt die 2 und macht ihn zu 3.
  6. Kaffee: Es nimmt die 3 und macht ihn zu 4.
  7. Es gibt einen Zähler für alle, deshalb sind die Nummern der Reihe nach.

Wenn das Feld public static ist, erreichst du es von außen über den Namen der Klasse (Product.nextId), nicht über ein Objekt.

Probier es aus

Gemeinsam und eigen

Was gibt das Programm aus?

class Order
{
public static int Count = 0;
public int Items = 0;
public Order()
{
Count++;
Items++;
}
}
Order a = new Order();
Order b = new Order();
Order c = new Order();
Console.WriteLine(Order.Count + " " + c.Items);
Antwort

Die Idee

Eine statische Methode: Arbeit ohne Objekt

Dasselbe gilt für Methoden. Eine static-Methode ist eine Methode der Klasse. Du rufst sie über den Namen der Klasse auf, ohne new:

class Calculator
{
public static int Add(int a, int b)
{
return a + b;
}
public static double Average(int[] values)
{
int sum = 0;
foreach (int v in values)
{
sum += v;
}
return (double)sum / values.Length;
}
}
Console.WriteLine(Calculator.Add(10, 20));
int[] grades = { 5, 6, 4, 6 };
Console.WriteLine(Calculator.Average(grades));
Console.WriteLine(Math.Max(3, 8));
Ausgabe
30
5.25
8

Calculator.Add braucht kein Objekt: Alles, was sie braucht, kommt als Parameter. Die Methoden von Math sind genauso: Math.Max, Math.Sqrt. Niemand schreibt new Math().

Jetzt ist klar, warum Main static ist. Wenn das Programm startet, gibt es noch keine Objekte. Die Laufzeitumgebung ruft Main direkt über die Klasse auf. Deshalb waren auch die Methoden aus dem Modul „Methoden“, die wir aus Main aufgerufen haben, static.

Genauer, für Neugierige

Umgekehrt geht es auch: In einer static-Methode kannst du ein Objekt erzeugen und seine Methoden aufrufen. Genau das machen wir in Main seit dem Anfang des Moduls.

Probier es aus

Finde den Fehler

Die Klasse lässt sich nicht kompilieren. In welcher Zeile ist der Fehler?

Wähle die Zeile mit dem Fehler.

Die Idee

Konstanten

Die MwSt. ist für alle Produkte 20%, und sie ändert sich nicht, während das Programm läuft. So ein Wert ist eine Konstante:

class Prices
{
public const double VatRate = 0.20;
public static double WithVat(double price)
{
return price * (1 + VatRate);
}
}
Console.WriteLine(Prices.WithVat(50));
Console.WriteLine("MwSt.: " + Prices.VatRate * 100 + "%");
Ausgabe
60
MwSt.: 20%

const bedeutet, dass der Wert beim Kompilieren bekannt ist und sich nicht ändern lässt. Ein const gehört immer zur Klasse, deshalb schreibst du kein static. Die Namen sehen aus wie Namen von Properties: VatRate.

Wenn jemand versucht, sie zu ändern,

Prices.VatRate = 0.25;

lässt sich das Programm nicht kompilieren:

Fehler beim Kompilieren
error CS0131: The left-hand side of an assignment must be a variable, property or indexer

Du gewinnst doppelt: Die Zahl 0.20 steht an einer Stelle mit einem klaren Namen, und niemand kann sie aus Versehen ändern.

Genauer, für Neugierige

Manchmal ist der Wert für jedes Objekt anders, darf sich aber nach dem Erzeugen des Objekts nicht ändern. So ist die Nummer des Produkts:

private static int nextId = 1;
private readonly int id;
public Product(string name)
{
this.name = name;
id = nextId;
nextId++;
}

Ein readonly-Feld wird einmal gesetzt, im Konstruktor, und ändert sich danach nicht. Es gibt auch static readonly: wie const, aber der Wert wird berechnet, wenn das Programm läuft, nicht beim Kompilieren. Du benutzt es für Werte, die nicht nur eine Zahl oder ein Text sind.

Probier es aus

Was fehlt?

Ein Jahr hat 12 Monate, und das wird sich nicht ändern. Was steht in der Lücke?
class Calendar
{
    public  int Months = 12;
}

Die Idee

Wann static und wann nicht

Frag dich: „Gehört das zu einem konkreten Objekt?“

  • Ja (der Kontostand des Kontos, der Name des Produkts, das Abheben von einem Konto): ein Feld oder eine Methode des Objekts, ohne static.
  • Nein (die Anzahl aller Konten, die MwSt., die Summe zweier Zahlen): static.

Ein häufiger Fehler ist, alles static zu machen, weil Main static ist und der Compiler dann aufhört, sich zu beschweren. Dann teilen sich alle „Objekte“ dieselben Daten, und du bist wieder beim Problem mit den parallelen Arrays aus der ersten Lektion.

Übung

Was alle Objekte teilen

Löse die Aufgaben in deinem Editor.

1. Bestellungen. Eine Klasse Order mit Kunde und Betrag. Jede neue Bestellung bekommt die nächste Nummer aus einem statischen Zähler. Eine schreibgeschützte statische Property Count sagt, wie viele Bestellungen erzeugt wurden.

Main
Order[] orders =
{
new Order("Maria", 45.5),
new Order("Peter", 120),
new Order("Elena", 18.9)
};
foreach (Order o in orders)
{
Console.WriteLine(o);
}
Console.WriteLine("Bestellungen: " + Order.Count);
Ausgabe
Bestellung Nr. 1: Maria, 45.5 €
Bestellung Nr. 2: Peter, 120 €
Bestellung Nr. 3: Elena, 18.9 €
Bestellungen: 3

2. Taschenrechner. Eine Klasse Calculator mit statischen Methoden zum Addieren, Subtrahieren und Multiplizieren ganzer Zahlen und zum Dividieren von Dezimalzahlen.

Main
Console.WriteLine(Calculator.Add(10, 20));
Console.WriteLine(Calculator.Subtract(10, 15));
Console.WriteLine(Calculator.Multiply(6, 7));
Console.WriteLine(Calculator.Divide(5, 2));
Ausgabe
30
-5
42
2.5

3. Wechselstube. Eine Klasse Converter mit Konstanten für den Kurs: 1 € = 1,08 US-Dollar und 1 € = 0,85 britische Pfund (die Kurse sind Beispiele). Statische Methoden rechnen einen Betrag in Euro in Dollar und in Pfund um. Für 20 €:

Ausgabe
20 EUR = 21.6 USD
20 EUR = 17 GBP

Weitere Aufgaben

  • Nummerierte Konten. Gib dem Account aus der Lektion „Kapselung“ eine Kontonummer aus einem statischen Zähler und ein readonly-Feld für die Nummer.
  • Umrechnen in beide Richtungen. Gib Converter Methoden, die von Dollar und von Pfund in Euro umrechnen. Rechne 20 € in Dollar um und zurück in Euro. Bekommst du genau 20?
  • Ein Limit. Der Shop nimmt höchstens 3 Produkte in einem Warenkorb an. Füge dafür eine Konstante hinzu und lass die Methode zum Hinzufügen das vierte ablehnen.

Kurz gesagt

Das Modul in kurz

  • Eine Klasse ist dein eigener Typ, und ein Objekt ist ein konkretes Exemplar davon, erzeugt mit new. Die Variable zeigt auf das Objekt. Klassen und Objekte
  • Der Konstruktor sorgt dafür, dass das Objekt vollständig entsteht. this ist das aktuelle Objekt. Konstruktoren und this
  • Instanzmethoden arbeiten mit den Feldern des Objekts. ToString() sagt, wie es als Text aussieht. Instanzmethoden
  • private versteckt die Felder. Von außen kommst du nur über Methoden und Properties an sie, die prüfen. Kapselung
  • static ist das, was die Klasse teilt: Zähler, Hilfsmethoden und Konstanten.

Daten und Regeln an einer Stelle, die niemand umgehen kann: Das ist objektorientierte Programmierung. Im nächsten Modul fangen Klassen an, Code miteinander zu teilen.

Test

Teste dich

1. Du erzeugst 5 Objekte aus einer Klasse mit dem Feld static int count. Wie viele Kopien von count liegen im Speicher?
2. Was kann eine statische Methode NICHT?
3. Warum ist Main static?
4. Was passiert bei Prices.VatRate = 0.25;?
5. Was ist ein guter Kandidat für eine statische Methode?

Erstellt von Ivo Gergov im Rahmen der Kampagne von Evo Tech für zugängliche Programmierausbildung ·