C# Konstruktor: der heimliche Held der Objekterzeugung
Konstruktor: Was passiert, wenn ein Objekt entsteht?
Im vorherigen Beitrag haben wir die Konzepte Klasse und Objekt kennengelernt. Wir haben Objekte erzeugt und ihren Eigenschaften Werte zugewiesen. Jedes Mal mussten wir aber Folgendes tun:
Car car1 = new Car();
car1.Brand = "Toyota";
car1.Model = "Corolla";
car1.Year = 2024;
car1.Color = "White";Vier Codezeilen, nur um ein einziges Auto zu erzeugen. Was, wenn wir 100 Autos brauchen? Und was, wenn wir vergessen, einzelne Eigenschaften zu setzen?
Genau das löst der Konstruktor.
Was ist ein Konstruktor?
Ein Konstruktor ist eine besondere Methode, die beim Erzeugen eines Objekts automatisch aufgerufen wird. Sein Name muss dem Klassennamen entsprechen, und er hat keinen Rückgabetyp.
public class Car
{
public string Brand { get; set; }
public string Model { get; set; }
public int Year { get; set; }
public string Color { get; set; }
// Constructor
public Car(string brand, string model, int year, string color)
{
Brand = brand;
Model = model;
Year = year;
Color = color;
Console.WriteLine($"🚗 New {brand} {model} manufactured!");
}
}
// Usage - Single line!
Car car1 = new Car("Toyota", "Corolla", 2024, "White");Sehen Sie? Eine Zeile statt vier. Und das Beste: Es ist nun unmöglich, eine Eigenschaft zu übersehen, weil der Konstruktor sie einfordert.
Der Standardkonstruktor: der stille Held
Schreiben Sie keinen eigenen Konstruktor, erzeugt C# im Hintergrund einen für Sie. Das ist der Standardkonstruktor.
public class Box
{
public int Width { get; set; }
public int Height { get; set; }
}
// C# creates this behind the scenes:
// public Box() { }
Box box1 = new Box(); // This works
box1.Width = 10;
box1.Height = 20;Aber Achtung! Sobald Sie einen eigenen Konstruktor schreiben, verschwindet der Standardkonstruktor:
public class Box
{
public int Width { get; set; }
public int Height { get; set; }
public Box(int width, int height)
{
Width = width;
Height = height;
}
}
Box box1 = new Box(); // ❌ ERROR! No parameterless constructor
Box box2 = new Box(10, 20); // ✅ WorksKonstruktor-Überladung: Auswahl anbieten
Manchmal möchten Sie den Nutzenden verschiedene Möglichkeiten bieten: „Sie können nur den Namen angeben – oder alles.“ Das nennt man Konstruktor-Überladung.
public class Player
{
public string Name { get; set; }
public int Level { get; set; }
public int Health { get; set; }
public string Class { get; set; }
// Constructor 1: Name only
public Player(string name)
{
Name = name;
Level = 1; // Default values
Health = 100;
Class = "Warrior";
}
// Constructor 2: Name and class
public Player(string name, string playerClass)
{
Name = name;
Class = playerClass;
Level = 1;
Health = 100;
}
// Constructor 3: Fully equipped
public Player(string name, string playerClass, int level, int health)
{
Name = name;
Class = playerClass;
Level = level;
Health = health;
}
public void ShowInfo()
{
Console.WriteLine($"⚔️ {Name} | Class: {Class} | Level: {Level} | Health: {Health}");
}
}Jetzt können Objekte ganz nach Bedarf erzeugt werden:
Player player1 = new Player("John");
Player player2 = new Player("Emma", "Mage");
Player player3 = new Player("Mike", "Archer", 50, 500);
player1.ShowInfo(); // ⚔️ John | Class: Warrior | Level: 1 | Health: 100
player2.ShowInfo(); // ⚔️ Emma | Class: Mage | Level: 1 | Health: 100
player3.ShowInfo(); // ⚔️ Mike | Class: Archer | Level: 50 | Health: 500Konstruktorverkettung mit this()
Ist Ihnen die Codewiederholung zwischen den Konstruktoren aufgefallen? Wir führen jedes Mal dieselben Zuweisungen aus. Mit this() rufen wir aus einem Konstruktor einen anderen auf:
public class Player
{
public string Name { get; set; }
public int Level { get; set; }
public int Health { get; set; }
public string Class { get; set; }
// Main constructor - All parameters
public Player(string name, string playerClass, int level, int health)
{
Name = name;
Class = playerClass;
Level = level;
Health = health;
Console.WriteLine($"✨ {name} joined the game!");
}
// Name only - Call main constructor
public Player(string name) : this(name, "Warrior", 1, 100)
{
// Extra code can be added here
}
// Name and class - Call main constructor
public Player(string name, string playerClass) : this(name, playerClass, 1, 100)
{
}
}Die Syntax : this(...) bedeutet: „Führe zuerst jenen Konstruktor aus und danach den Rumpf dieses Konstruktors.“
Optionale Parameter: der moderne Weg
Eine schöne Eigenschaft von C#: optionale Parameter. Damit erreichen Sie dieselbe Flexibilität mit einem einzigen Konstruktor statt mit Überladungen.
public class Product
{
public string Name { get; set; }
public decimal Price { get; set; }
public int Stock { get; set; }
public string Category { get; set; }
public bool IsActive { get; set; }
public Product(
string name,
decimal price,
int stock = 0, // Default: 0
string category = "General", // Default: "General"
bool isActive = true) // Default: true
{
Name = name;
Price = price;
Stock = stock;
Category = category;
IsActive = isActive;
}
}
// Different usage patterns
Product product1 = new Product("Laptop", 1500);
Product product2 = new Product("Mouse", 50, 100);
Product product3 = new Product("Keyboard", 80, 50, "Electronics");
Product product4 = new Product("Monitor", 800, category: "Electronics"); // Named parameterDank benannter Parameter können Sie mittlere Parameter überspringen. Sehr praktisch!
Praxisbeispiel: ein Bestellsystem im E-Commerce
Fügen wir alles Gelernte zusammen. Stellen Sie sich vor, in einem echten E-Commerce-System werden Bestellungen erzeugt:
public class Order
{
public string OrderNumber { get; private set; }
public string CustomerName { get; set; }
public List<string> Products { get; set; }
public decimal TotalAmount { get; set; }
public DateTime CreatedDate { get; private set; }
public string Status { get; private set; }
// Private constructor - Cannot be created directly from outside
private Order()
{
OrderNumber = GenerateOrderNumber();
CreatedDate = DateTime.Now;
Status = "Pending";
Products = new List<string>();
}
// Public constructor - Standard order
public Order(string customerName) : this()
{
CustomerName = customerName;
}
// Fully equipped order
public Order(string customerName, List<string> products, decimal totalAmount) : this()
{
CustomerName = customerName;
Products = products;
TotalAmount = totalAmount;
}
private string GenerateOrderNumber()
{
return $"ORD-{DateTime.Now:yyyyMMdd}-{new Random().Next(1000, 9999)}";
}
public void AddProduct(string productName, decimal price)
{
Products.Add(productName);
TotalAmount += price;
Console.WriteLine($"✅ '{productName}' added to cart. Total: ${TotalAmount:F2}");
}
public void ShowOrderSummary()
{
Console.WriteLine("\n" + new string('=', 40));
Console.WriteLine($"📦 ORDER DETAILS");
Console.WriteLine(new string('=', 40));
Console.WriteLine($"Order No: {OrderNumber}");
Console.WriteLine($"Customer: {CustomerName}");
Console.WriteLine($"Date: {CreatedDate:MM/dd/yyyy HH:mm}");
Console.WriteLine($"Status: {Status}");
Console.WriteLine("\nProducts:");
foreach (var product in Products)
{
Console.WriteLine($" • {product}");
}
Console.WriteLine($"\n💰 TOTAL: ${TotalAmount:F2}");
Console.WriteLine(new string('=', 40));
}
}Verwendung:
// Start empty order
Order order1 = new Order("John Smith");
order1.AddProduct("iPhone 15", 999);
order1.AddProduct("AirPods Pro", 249);
order1.AddProduct("MagSafe Charger", 39);
order1.ShowOrderSummary();
// Order with ready product list
var products = new List<string> { "MacBook Pro", "Magic Mouse", "USB-C Hub" };
Order order2 = new Order("Emma Johnson", products, 2500);
order2.ShowOrderSummary();Ausgabe:
✅ 'iPhone 15' added to cart. Total: $999.00
✅ 'AirPods Pro' added to cart. Total: $1248.00
✅ 'MagSafe Charger' added to cart. Total: $1287.00
========================================
📦 ORDER DETAILS
========================================
Order No: ORD-20260203-4721
Customer: John Smith
Date: 02/03/2026 14:30
Status: Pending
Products:
• iPhone 15
• AirPods Pro
• MagSafe Charger
💰 TOTAL: $1287.00
========================================Statischer Konstruktor: läuft beim Laden der Klasse
Manchmal soll etwas geschehen, wenn eine Klasse zum ersten Mal genutzt wird. Dafür ist der statische Konstruktor perfekt:
public class Database
{
public static string ConnectionString { get; private set; }
public static bool IsConnected { get; private set; }
// Static constructor - Runs once when class is first used
static Database()
{
Console.WriteLine("🔌 Establishing database connection...");
ConnectionString = "Server=localhost;Database=MyApp;";
IsConnected = true;
Console.WriteLine("✅ Connection ready!");
}
public static void ExecuteQuery(string sql)
{
if (IsConnected)
{
Console.WriteLine($"📊 Executing query: {sql}");
}
}
}
// Static constructor runs on first use
Database.ExecuteQuery("SELECT * FROM Users");
Database.ExecuteQuery("SELECT * FROM Products");
// Output:
// 🔌 Establishing database connection...
// ✅ Connection ready!
// 📊 Executing query: SELECT * FROM Users
// 📊 Executing query: SELECT * FROM ProductsDer statische Konstruktor:
- Kann keine Parameter entgegennehmen
- Hat keinen Zugriffsmodifizierer
- Läuft genau einmal
- Wir können den Zeitpunkt nicht steuern (das entscheidet die CLR)
Validierung im Konstruktor: Robustheit zahlt sich aus
Der Konstruktor ist der ideale Ort, um beim Erzeugen eines Objekts die übergebenen Daten zu prüfen:
public class Email
{
public string Address { get; private set; }
public string Domain { get; private set; }
public Email(string address)
{
if (string.IsNullOrWhiteSpace(address))
throw new ArgumentException("Email address cannot be empty!");
if (!address.Contains("@"))
throw new ArgumentException("Invalid email format!");
if (address.Length < 5)
throw new ArgumentException("Email address is too short!");
Address = address.ToLower().Trim();
Domain = Address.Split('@')[1];
Console.WriteLine($"✅ Email created: {Address}");
}
}
try
{
Email email1 = new Email("[email protected]"); // ✅ Works
Email email2 = new Email("invalid-email"); // ❌ Throws error
}
catch (ArgumentException ex)
{
Console.WriteLine($"❌ Error: {ex.Message}");
}Zusammenfassung: Arten von Konstruktoren
| Art | Beschreibung | Beispiel |
|---|---|---|
| Standard | Ohne Parameter, wird von C# automatisch erzeugt | public Class() {} |
| Parametrisiert | Nimmt Werte entgegen | public Class(int x) {} |
| Überladen | Mehrere Konstruktoren | Unterschiedliche Parameterkombinationen |
| Verkettet | Ruft einen anderen Konstruktor auf | : this(...) |
| Statisch | Läuft beim Laden der Klasse | static Class() {} |
| Privat | Von außen nicht zugänglich | Für das Factory-Pattern |
Nächster Schritt: Zugriffsmodifizierer
Mit Konstruktoren erzeugen wir unsere Objekte sicherer und einheitlicher.
Im nächsten Beitrag behandeln wir Zugriffsmodifizierer. Was bedeuten public, private und protected? Warum ist es eine schlechte Idee, alles public zu machen? Und wofür sind Getter und Setter da?
Schreiben Sie weiter sauberen Code! 🚀
Dies ist der dritte Beitrag der Reihe „OOP mit C#“. Im vorherigen Beitrag ging es um Klasse und Objekt, im nächsten um Zugriffsmodifizierer.