Häufige C#-Anfängerfehler & Gotchas
Wenn dein C#-Code aus den Einsteiger-Seiten plötzlich nicht mehr tut, was du erwartest, liegt das fast immer an einer Handvoll wiederkehrender Stolpersteine. Manche werfen eine Exception und reißen die Action ab, andere scheitern komplett still: kein Fehler, keine Chat-Nachricht, einfach nichts. Diese Seite sammelt die acht häufigsten davon, jeweils mit kurzer Ursache und dem passenden Fix als Mini-Snippet. Eine Checkliste zum Durchgehen, wenn etwas hakt.
Doku: docs.streamer.bot · C# Methoden
Die Checkliste
Abschnitt betitelt „Die Checkliste“Die Kurzfassung als Tabelle. Darunter folgt jeder Punkt einzeln mit Code.
| # | Problem | Fix |
|---|---|---|
| 1 | Direkter args["x"]-Zugriff crasht, wenn der Key fehlt | CPH.TryGetArg(...) mit out und Default |
| 2 | Versehentliches return false stoppt die Action-Kette | Im Erfolgsfall immer return true |
| 3 | GetGlobalVar<int> ohne Null-Absicherung wirft | GetGlobalVar<int?>(...) ?? 0 |
| 4 | await im synchronen Execute() kompiliert nicht | .GetAwaiter().GetResult() |
| 5 | new Random() pro Aufruf liefert schlechte Zufallszahlen | CPH.Between(...) nutzen |
| 6 | CPH.Wait(...) blockiert den ausführenden Worker | Sparsam einsetzen, lange Pausen vermeiden |
| 7 | Tippfehler in Variablen- oder Action-Name scheitern still | Namen in const string zentral ablegen |
| 8 | Compile-Fehler landen im Log, nicht im Chat | Beim Speichern aufs Compile-Ergebnis und Log achten |
1. Direkter args-Zugriff crasht
Abschnitt betitelt „1. Direkter args-Zugriff crasht“Der häufigste harte Crash. Du greifst per Indexer direkt auf args["rawInput"] zu, aber bei diesem Trigger gibt es das Argument gar nicht. Das wirft eine KeyNotFoundException, die Action bricht ab.
// FALSCH: crasht, wenn "rawInput" fehltstring text = args["rawInput"].ToString();Der Fix ist CPH.TryGetArg. Die Methode gibt einen bool zurück, ob das Argument existiert, und schreibt den Wert über den out-Parameter. So fängst du das fehlende Argument sauber ab, statt zu crashen.
// RICHTIG: prüft erst, ob das Argument da istif (!CPH.TryGetArg("rawInput", out string text)) { text = "";}Mehr dazu auf Argumente lesen.
2. Versehentliches return false
Abschnitt betitelt „2. Versehentliches return false“Der Rückgabewert von Execute() ist kein Statusflag für dich, sondern steuert die Action-Kette. Ein return false sagt Streamer.bot: brich diese Action hier ab, führe die nachfolgenden Sub-Actions nicht mehr aus. Wenn du also versehentlich aus einem else-Zweig false zurückgibst, hängt der Rest deiner Action in der Luft, ohne erkennbaren Grund.
public bool Execute() { if (!CPH.TryGetArg("user", out string user)) { return false; // ok hier: kein User, Kette absichtlich stoppen } CPH.SendMessage($"Hi {user}!"); return true; // Erfolg: Kette läuft weiter}Faustregel: Im Erfolgsfall immer return true. return false nur, wenn du die nachfolgenden Sub-Actions bewusst überspringen willst. Details auf return true / false.
3. GetGlobalVar ohne Null-Absicherung
Abschnitt betitelt „3. GetGlobalVar ohne Null-Absicherung“Wenn ein Global noch nie gesetzt wurde, gibt GetGlobalVar keinen Wert zurück, sondern null. Liest du das in einen nicht-nullbaren int, scheitert das Entpacken des null-Werts und du bekommst eine NullReferenceException. Rechnest du danach weiter, crasht die Action.
// FALSCH: beim ersten Aufruf existiert der Global nicht, null entpackt nichtint n = CPH.GetGlobalVar<int>("count", true);Lies in einen Nullable-Typ und fang null mit dem ??-Operator ab. Nur GetGlobalVar<int?> (mit Fragezeichen) kann null sauber zurückgeben und damit den Default-Zweig auslösen.
// RICHTIG: null wird zu 0int n = CPH.GetGlobalVar<int?>("count", true) ?? 0;Ausführlich auf Globale Variablen.
4. await geht im Execute nicht
Abschnitt betitelt „4. await geht im Execute nicht“Execute() ist eine synchrone Methode, sie gibt bool zurück, nicht Task<bool>. Deshalb kannst du darin kein await schreiben, der Code kompiliert nicht. Das betrifft jede async-API wie HttpClient mit GetStringAsync oder PostAsync.
// FALSCH: kompiliert nicht, Execute ist synchronstring json = await client.GetStringAsync("https://...");Du überbrückst die async-Methode mit .GetAwaiter().GetResult(). Das blockiert den aktuellen Thread, bis das Ergebnis da ist, und liefert den Wert synchron zurück.
// RICHTIG: blockierend überbrückenstring json = client.GetStringAsync("https://...").GetAwaiter().GetResult();Mehr zum HttpClient-Muster auf HttpClient & APIs.
5. new Random pro Aufruf
Abschnitt betitelt „5. new Random pro Aufruf“Ein klassischer Anfängerfehler aus dem allgemeinen C#: Du legst bei jedem Execute() ein frisches new Random() an. Da der Standard-Seed auf der Systemzeit beruht und mehrere Aufrufe in derselben Millisekunde landen können, bekommst du dann mehrfach dieselbe Zahl.
// FALSCH: neuer Seed pro Aufruf, schlechte Verteilungint roll = new Random().Next(1, 7);In Streamer.bot brauchst du das gar nicht selbst zu lösen. CPH.Between(min, max) liefert eine Zufallszahl, und beide Grenzen sind laut Doku inklusiv. Für einen Wurf von 1 bis 6 also:
// RICHTIG: 1 bis 6, beide Grenzen inklusiveint roll = CPH.Between(1, 6);Für eine Wahrscheinlichkeit zwischen 0.0 und 1.0 gibt es CPH.NextDouble(). Mehr dazu auf Zufall & Wartezeiten.
6. CPH.Wait blockiert den Worker
Abschnitt betitelt „6. CPH.Wait blockiert den Worker“CPH.Wait(int milliseconds) pausiert die Ausführung, und zwar den ausführenden Worker-Thread der Queue. Solange die Action wartet, kann dieser Worker keine andere Action abarbeiten. Ein CPH.Wait(10000) legt also für zehn Sekunden alles lahm, was über dieselbe Queue läuft.
// VORSICHT: blockiert den Worker für 5 SekundenCPH.Wait(5000);Kurze Pausen sind in Ordnung. Für lange Verzögerungen oder regelmäßige Aktionen sind eigene Action-Queues oder Timer die bessere Wahl. Eine Action, die du mit CPH.RunAction("Name", false) startest, läuft in ihrer eigenen Queue, und dein Code wartet nicht auf sie. Hintergründe auf Threading & Performance und Zufall & Wartezeiten.
7. Name-Tippfehler scheitern still
Abschnitt betitelt „7. Name-Tippfehler scheitern still“Globals und Actions werden über exakte Strings angesprochen. Schreibst du aceCount und liest acecount, zeigt das auf einen leeren Global und du bekommst kommentarlos den Default. Genauso bei CPH.RunAction("Shoutot"): ein Tippfehler im Action-Namen, und es passiert einfach nichts, ohne Fehlermeldung.
// FALSCH: Tippfehler im Namen, scheitert stillCPH.SetGlobalVar("aceCount", 1, true);int n = CPH.GetGlobalVar<int?>("acecount", true) ?? 0; // immer 0Leg jeden Namen einmal in eine const string und verwende überall nur diese. Ein Tippfehler wird dann zum Compile-Fehler, statt still zu scheitern.
// RICHTIG: ein Name, eine Quelleconst string AceCountVar = "aceCount";CPH.SetGlobalVar(AceCountVar, 1, true);int n = CPH.GetGlobalVar<int?>(AceCountVar, true) ?? 0;CPH.RunAction gibt zusätzlich einen bool zurück, ob die Action gefunden und ausgelöst wurde. Den kannst du prüfen und ins Log schreiben. Mehr auf Actions auslösen.
8. Compile-Fehler landen im Log
Abschnitt betitelt „8. Compile-Fehler landen im Log“Wenn dein C#-Code einen Syntax- oder Typfehler hat, kompiliert er nicht und die Action läuft gar nicht erst an. Im Chat siehst du davon nichts: keine Nachricht, keine Reaktion. Die Compile-Fehler mit Zeilennummer stehen im Streamer.bot-Log, nicht im Twitch-Chat.
[ERROR] Compiler error (CS1002): ; expected (Zeile 12)Beim Speichern der C#-Action zeigt Streamer.bot bereits, ob der Code erfolgreich kompiliert. Schlägt zur Laufzeit trotzdem etwas fehl, ist das Log die erste Anlaufstelle. Schreib dir mit CPH.LogInfo(...) Zwischenwerte mit, um die problematische Stelle einzugrenzen. Komplett auf Logging & Debugging.
Häufige Fallen
Abschnitt betitelt „Häufige Fallen“args["x"]stattTryGetArg: Direkter Indexer-Zugriff crasht bei fehlendem Key. ImmerCPH.TryGetArgmitoutund Default nutzen.return falseals Gewohnheit: Im Erfolgsfall gehörtreturn truehin, sonst stoppt die Action-Kette.GetGlobalVar<int>ohne?: Ein nicht gesetzter Global kommt alsnullzurück. ImmerGetGlobalVar<int?>(...) ?? 0lesen.awaitimExecute(): Geht nicht,Execute()ist synchron. Async-Aufrufe mit.GetAwaiter().GetResult()überbrücken.- Eigenes
new Random(): Unnötig und fehleranfällig.CPH.Between(min, max)nehmen, beide Grenzen inklusive. - Stille Tippfehler: Falsch geschriebene Variablen- oder Action-Namen scheitern ohne Fehler. Namen in
const stringzentralisieren. - Chat statt Log prüfen: Compile- und Laufzeitfehler stehen im Streamer.bot-Log, nie im Chat.