İçeriğe geç / Skip to content / Zum Inhalt
Ahmet Balaman LogoAhmet Balaman

C# Konstruktor: der heimliche Held der Objekterzeugung

Ahmet Balaman
C#OOPConstructor.NETProgramming Fundamentals

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);  // ✅ Works

Konstruktor-Ü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: 500

Konstruktorverkettung 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 parameter

Dank 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 15AirPods ProMagSafe 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 Products

Der 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.

Kommentare