Modern, vielseitig, die Sprache der .NET-Welt
Ausführen: .NET SDK installieren (dotnet.microsoft.com). Dann im Terminal dotnet new console -o hallo, in den Ordner wechseln und dotnet run ausführen. Der Code steht in Program.cs.
dotnet new console, dotnet run sind die wichtigsten Befehledotnet --version # Version prüfen
dotnet new console -o hallo # neues Projekt
cd hallo
dotnet run # übersetzen und ausführenint, double, decimal (Geld), bool, char, string; var lässt den Compiler den Typ erkennen$"{name}" mit Formaten (:F2, :C, :D4)TryParse ist die sichere Umwandlung von Eingabendecimal für Geld, double für Wissenschaftchecked erkennt Überläufeint alter = 17;
double preis = 19.99;
decimal betrag = 19.99m; // exakt, für Geld
bool fertig = true;
char buchstabe = 'A';
string name = "Mia";
var zahl = 42; // var: Typ wird erkannt (int)
const double Pi = 3.14159;
Console.WriteLine($"{name} ist {alter} Jahre alt"); // String-Interpolation
Console.WriteLine($"Preis: {preis:F2} / {betrag:C} / {alter,5} / {alter:D4}");
Console.WriteLine("Summe: " + (zahl + 1) + " " + fertig + " " + buchstabe + " " + Pi);
Console.Write("ohne Zeilenumbruch, ");
Console.WriteLine("dann weiter");??, ?., ??= gehen mit null um; z[^1] und z[1..4] adressieren von hinten und Bereicheswitch-Ausdrücke mit => und Mustern (>= 90, or, and, _) sind kompakt und vollständigforeach ist die Standardschleife über Sammlungenbreak, continue wie in C; kein Durchfallen im switchint a = 17, b = 5;
Console.WriteLine($"{a + b} {a / b} {a % b} {(double) a / b:F2}");
Console.WriteLine($"{a > b && b > 0} {a < b || b == 5} {!(a > b)}");
string status = a >= 18 ? "volljährig" : "minderjährig";
Console.WriteLine(status);
string? name = null;
Console.WriteLine(name ?? "Gast"); // Ersatzwert bei null
Console.WriteLine(name?.Length ?? 0); // sicherer Zugriff
name ??= "Standard"; // nur zuweisen, wenn null
Console.WriteLine(name);
var z = new[] { 10, 20, 30, 40, 50 };
Console.WriteLine($"{z[^1]} {z[^2]} {string.Join(",", z[1..4])} {string.Join(",", z[..2])}");== vergleicht den InhaltStringComparison steuert Groß-/Kleinschreibung und KulturSplit, Join, Substring, Replace, Trim, PadLeft, Contains sind AlltagswerkzeugeStringBuilder für viele Änderungen; Rohtext """ für mehrzeiligen Text:F2, :N0, :P1, :D5 und Kultur beachtenSystem.Text.RegularExpressionsstring s = "C# macht Spaß";
Console.WriteLine($"{s.Length} {s[0]} {s.IndexOf("macht")} {s.IndexOf('x')}");
Console.WriteLine($"{s.ToUpper()} | {s.ToLower()}");
Console.WriteLine($"{s.Substring(3)} | {s[..2]} | {s.Substring(3, 5)}");
Console.WriteLine($"{s.Replace("a", "4")} | {s.Contains("Spaß")} | {s.StartsWith("C#")} | {s.EndsWith("ß")}");
Console.WriteLine("[" + " Leerraum ".Trim() + "][" + " x ".TrimStart() + "]");
Console.WriteLine(string.Join(", ", new[] { "a", "b", "c" }) + " " + new string('-', 5) + " " + "ab".PadLeft(5, '.') + " " + "7".PadLeft(3, '0'));
Console.WriteLine(string.IsNullOrEmpty("") + " " + string.IsNullOrWhiteSpace(" "));=> ausdruck für kurze Methoden; Standardwerte und benannte Argumenteref, out, in, params ändern die Übergabe; TryXyz(..., out var ergebnis) ist ein bekanntes Muster(int min, int max); Dekonstruktion mit var (a, b) = ...int Addiere(int a, int b) => a + b; // Ausdruckskörper
void Gruss(string name) { Console.WriteLine($"Hallo {name}!"); }
double Potenz(double basis, int exponent = 2) // Standardwert
{
double r = 1;
for (int i = 0; i < exponent; i++) r *= basis;
return r;
}
Console.WriteLine(Addiere(3, 4));
Gruss("Mia");
Console.WriteLine($"{Potenz(3)} {Potenz(2, 10)} {Potenz(exponent: 3, basis: 2)}"); // benannte ArgumenteList<T> wächst; Dictionary<K, V> ordnet Schlüssel Werten zuTryGetValue und GetValueOrDefault statt Zugriff, der einen Fehler werfen kannHashSet, Queue, Stack, PriorityQueue decken Mengen und Warteschlangen ab[1, 2, 3] und Spread .. (C# 12)IEnumerable<T>/IReadOnlyList<T> anbietenint[] zahlen = { 5, 3, 9, 1, 7 };
string[] namen = new string[3];
namen[0] = "Mia";
Console.WriteLine($"{zahlen[0]} {zahlen.Length} {namen[1] ?? "leer"} {zahlen[^1]}");
Array.Sort(zahlen);
Console.WriteLine(string.Join(" ", zahlen));
Array.Reverse(zahlen);
Console.WriteLine(string.Join(" ", zahlen) + " | " + Array.IndexOf(zahlen, 9));
int[,] matrix = { { 1, 2, 3 }, { 4, 5, 6 } }; // zweidimensional
Console.WriteLine($"{matrix[1, 2]} {matrix.GetLength(0)} {matrix.GetLength(1)}");
int[][] gezackt = { new[] { 1 }, new[] { 2, 3 } }; // Array von Arrays
Console.WriteLine(gezackt[1][1]);
try { Console.WriteLine(zahlen[10]); }
catch (IndexOutOfRangeException e) { Console.WriteLine("Fehler: " + e.Message); }get/set/init), Methoden, Konstruktorenprivate set, init und required steuern Veränderbarkeitstatic gehört zur Klasse; const ist eine Compile-Zeit-Konstante: this(...)); Primärkonstruktoren seit C# 12IDisposable + using räumen Ressourcen deterministisch aufvar rex = new Hund("Rex", 3);
var mimi = new Hund("Mimi", 5) { Rasse = "Siamkatze" }; // Objekt-Initialisierer
rex.Geburtstag();
Console.WriteLine(rex.Bellen());
Console.WriteLine($"{rex} | {mimi.Rasse} | {rex.Alter}");
class Hund
{
public string Name { get; } // nur lesbar (Eigenschaft)
public int Alter { get; private set; } // von außen nur lesbar
public string Rasse { get; set; } = "unbekannt"; // lesen und schreiben, mit Standardwert
public Hund(string name, int alter) // Konstruktor
{
Name = name;
Alter = alter;
}
public void Geburtstag() => Alter++;
public string Bellen() => $"{Name} sagt: Wuff!";
public override string ToString() => $"Hund[{Name}, {Alter}]";
}virtual/override/base/sealed steuern Vererbung; eine Basisklasse, viele Interfacesabstract für Vorlagen mit Pflicht-MethodenIName) beschreiben Verträge, auch mit StandardimplementierungenIComparable<T>, IEnumerable<T>, IDisposable sind die wichtigsten Standard-InterfacesList<Tier> tiere = [new Hund("Rex"), new Katze("Mimi"), new Tier("Etwas")];
foreach (var t in tiere) Console.WriteLine(t.Vorstellen()); // Polymorphie
class Tier
{
public string Name { get; }
public Tier(string name) => Name = name;
public virtual string Laut() => "..."; // virtual: darf überschrieben werden
public string Vorstellen() => $"{Name} macht {Laut()}";
}
class Hund : Tier
{
public Hund(string name) : base(name) { } // Konstruktor der Basis
public override string Laut() => "Wuff";
}
class Katze : Tier
{
public Katze(string name) : base(name) { }
public override string Laut() => "Miau";
}record erzeugt Eigenschaften, Gleichheit, ToString und with-Kopienstruct ist ein Werttyp: Zuweisung kopiertenum mit [Flags] kombiniert Optionenswitch, is) prüft Typ, Wert, Eigenschaften und Listen(Name: "x", Alter: 1) sind praktische kleine Datenträgervar mia = new Person("Mia", 17);
var tom = mia with { Name = "Tom" }; // Kopie mit Änderung
Console.WriteLine(mia);
Console.WriteLine($"{tom} {mia == new Person("Mia", 17)} {ReferenceEquals(mia, new Person("Mia", 17))}");
var (name, alter) = mia; // Dekonstruktion
Console.WriteLine($"{name} {alter}");
record Person(string Name, int Alter);string? kann null sein; ??, ?., is not null behandeln estry/catch/finally; spezifische catch-Blöcke vor allgemeinen; when filtertExceptionthrow; bewahrt den Stacktrace; Ursache über innerExceptionTryXyz-Methoden vermeiden Ausnahmen im Normalfallstring? eingabe = null;
string name = eingabe ?? "Gast"; // Ersatzwert
int laenge = eingabe?.Length ?? 0; // sicherer Zugriff
Console.WriteLine($"{name} {laenge}");
if (eingabe is not null) Console.WriteLine(eingabe.ToUpper()); // Compiler weiß: hier nicht null
else Console.WriteLine("keine Eingabe");
string sicher = eingabe!; // ! = "ich weiß es besser" (Warnung unterdrücken, Vorsicht!)
Console.WriteLine(sicher is null);
ArgumentNullException.ThrowIfNull(name); // Parameter prüfen
try { Pruefe(null!); } catch (ArgumentNullException e) { Console.WriteLine(e.ParamName); }
void Pruefe(string text) { ArgumentNullException.ThrowIfNull(text); }class Stapel<T>, T Methode<T>(T x); where T : ... schränkt einWhere, Select, OrderBy, GroupBy, Sum, Any, ToList und mehrforeach/ToList/Countyield return erzeugt eigene SequenzenConsole.WriteLine(Groesser(3, 9) + " " + Groesser("kiwi", "apfel") + " " + Groesser(2.5, 1.5));
var s = new Stapel<int>();
s.Push(1); s.Push(2); s.Push(3);
Console.WriteLine($"{s.Pop()} {s.Anzahl}");
var t = new Stapel<string>();
t.Push("a");
Console.WriteLine(t.Pop());
T Groesser<T>(T a, T b) where T : IComparable<T> => a.CompareTo(b) >= 0 ? a : b;
class Stapel<T>
{
private readonly List<T> _daten = [];
public int Anzahl => _daten.Count;
public void Push(T x) => _daten.Add(x);
public T Pop()
{
if (_daten.Count == 0) throw new InvalidOperationException("leer");
T x = _daten[^1];
_daten.RemoveAt(_daten.Count - 1);
return x;
}
}Func<..., R>, Action<...> und Predicate<T> decken fast alle Delegate-Fälle ab(a, b) => a + b fangen Variablen ihrer Umgebung ein (Closure)event EventHandler<T>) benachrichtigen Beobachter: += zum Anmelden, -= zum AbmeldenFunc<int, int> quadrat = x => x * x;
Func<int, int, int> plus = (a, b) => a + b;
Action<string> ausgabe = s => Console.WriteLine("> " + s);
Predicate<string> leer = string.IsNullOrEmpty;
ausgabe($"{quadrat(7)} {plus(3, 4)} {leer("")}");
int faktor = 10;
Func<int, int> mal = x => x * faktor; // Closure: fängt faktor ein
faktor = 3;
Console.WriteLine(mal(5));
Func<int, Func<int, int>> addierer = a => b => a + b; // Funktion, die eine Funktion liefert
var plus5 = addierer(5);
Console.WriteLine(plus5(10));
var woerter = new List<string> { "Birne", "Apfel", "Kiwi" };
woerter.Sort((a, b) => a.Length.CompareTo(b.Length));
woerter.ForEach(w => Console.Write(w + " "));
Console.WriteLine();
static int Rechne(Func<int, int, int> op, int a, int b) => op(a, b);
Console.WriteLine($"{Rechne((a, b) => a * b, 6, 7)} {Rechne(Math.Max, 3, 9)}"); // Methodengruppe als Delegateasync/await wartet, ohne Threads zu blockieren; Task<T> ist das Versprechen eines ErgebnissesTask.WhenAlltry/catch rund um awaitCancellationToken für Abbruch und Zeitlimits.Result/.Wait(); async Task statt async voidParallel.For, Interlocked, lock für CPU-Arbeit und gemeinsame DatenConsole.WriteLine("start");
string ergebnis = await LadeAsync("Daten");
Console.WriteLine(ergebnis);
async Task<string> LadeAsync(string name)
{
await Task.Delay(100); // simuliert Wartezeit, blockiert keinen Thread
return $"{name} geladen";
}File, Directory, Path, StreamReader/StreamWriter für Dateien; Path.Combine für Pfade; using für StreamsSystem.Text.Json: Serialize, Deserialize, JsonDocumentDateTime, DateOnly, TimeOnly, DateTimeOffset, TimeSpan für Zeit; Zeitpunkte in UTC speichernToString("dd.MM.yyyy"); Kultur beachtenIOException und Co. abstring ordner = Path.Combine(Path.GetTempPath(), "kurs-demo");
Directory.CreateDirectory(ordner);
string datei = Path.Combine(ordner, "notiz.txt");
File.WriteAllText(datei, "Zeile 1\nZeile 2\n");
File.AppendAllText(datei, "Zeile 3\n");
string inhalt = File.ReadAllText(datei);
string[] zeilen = File.ReadAllLines(datei);
Console.WriteLine($"{zeilen.Length} Zeilen, {inhalt.Length} Zeichen");
Console.WriteLine(File.Exists(datei) + " " + Path.GetFileName(datei) + " " + Path.GetExtension(datei));
foreach (var z in File.ReadLines(datei).Where(z => z.Contains('3'))) Console.WriteLine(z.Trim()); // zeilenweise, lazy
using (var schreiber = new StreamWriter(Path.Combine(ordner, "stream.txt")))
{
schreiber.WriteLine("über einen Stream");
}
using (var leser = new StreamReader(Path.Combine(ordner, "stream.txt")))
{
string? zeile;
while ((zeile = leser.ReadLine()) != null) Console.WriteLine(zeile);
}
Console.WriteLine(string.Join(",", Directory.GetFiles(ordner).Select(Path.GetFileName).OrderBy(n => n)));
Directory.Delete(ordner, recursive: true);
Console.WriteLine(Directory.Exists(ordner));dotnet new/run/build/test/publish/add package sind die Standardbefehle.csproj enthält Framework, Nullable und NuGet-Pakete[Fact], [Theory], Assert.Equal; Arrange–Act–Assert<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
</Project>// Variablen und Konstanten
var x = 5; const int Max = 10; string? n = null;
// Kontrolle
if (...) { } else if (...) { } else { }
var ergebnis = wert switch { > 10 => "gross", _ => "klein" };
for (...) { } foreach (var e in liste) { } while (...) { } do { } while (...);
// Methoden
int Add(int a, int b) => a + b;
void Tausche(ref int a, ref int b) { }
bool TryX(string s, out int w) { w = 0; return true; }
// Klassen
class A : B, IX { public int P { get; set; } public A() { } public override string ToString() => ""; }
record R(string Name, int Alter);
struct S { public int X; }
enum E { Eins, Zwei }
interface IX { void M(); }
// Fehler
try { } catch (Exception e) when (...) { } finally { }
throw new ArgumentException("...");
// Async
async Task<int> LadeAsync() { await Task.Delay(1); return 1; }
// LINQ
liste.Where(x => x > 1).Select(x => x * 2).OrderBy(x => x).ToList();
// Null
a ?? b; a?.B; a ??= b; a!;
// Muster
if (o is string s && s.Length > 0) { }