Veröffentlicht · wird verbessert
C#-Anleitung · 3/6
Dieses Kapitel ist vorerst nur auf Englisch verfügbar.
Every C# type is either a value type or a reference type, and that decides what happens when you assign, pass, or compare values. This chapter covers that split, classes, structs, records, properties, common collections, and null safety.
Built-in types such as int, double, bool, decimal, and DateTime, along with any struct or enum, are value types: the variable holds the data itself, so assignment copies it. Classes, string, arrays, and record classes are reference types: the variable holds a reference to an object, so assignment makes two variables point at the same object.
var p1 = new PointStruct { X = 1 };
var p2 = p1; // copies the value
p2.X = 99;
Console.WriteLine(p1.X); // 1
var c1 = new PointClass { X = 1 };
var c2 = c1; // copies the reference
c2.X = 99;
Console.WriteLine(c1.X); // 99
struct PointStruct { public int X; }
class PointClass { public int X; }string is a reference type, but it is immutable and == compares contents, so it behaves like a value. Use structs for small, immutable data such as coordinates; classes are the default otherwise.
A class bundles data (fields and properties) with behavior (methods). Public data is exposed through properties rather than raw fields. A property has get and set accessors, so you can add validation later without breaking any calling code.
var account = new Account("Dana") { Number = "110-123-456" };
account.Deposit(500m);
Console.WriteLine($"{account.Owner}: {account.Balance:F2}");
public class Account
{
private decimal _balance;
public Account(string owner) => Owner = owner;
public string Owner { get; } // read-only property
public string? Memo { get; set; } // auto-implemented property
public required string Number { get; init; } // must be set at creation, then frozen
public decimal Balance => _balance; // computed property
public void Deposit(decimal amount)
{
if (amount <= 0)
throw new ArgumentOutOfRangeException(nameof(amount), "Deposit must be positive.");
_balance += amount;
}
}An init accessor allows assignment only during object initialization (the { Number = ... } part), and required turns a forgotten assignment into a compile error. Notice that the statements come first and the class declaration follows: in a file with top-level statements, type declarations must come after the statements.
Records are a concise way to declare types whose main job is carrying data. A positional record gets a constructor, read-only properties, value-based equality, and a readable for free.
ToStringvar a = new Product("Mouse", 25.00m);
var b = new Product("Mouse", 25.00m);
Console.WriteLine(a == b); // True: same contents means equal
Console.WriteLine(a); // Product { Name = Mouse, Price = 25.00 }
var onSale = a with { Price = 19.99m }; // copy with one property changed
public record Product(string Name, decimal Price);A with expression leaves the original untouched and returns a new record with the listed properties replaced. Plain record declares a reference type; write record struct when you want a value type instead.
The workhorses live in System.Collections.Generic: List<T> and Dictionary<TKey, TValue>. The type parameters in angle brackets let the compiler check what goes in and what comes out.
List<string> langs = ["C#", "F#", "Rust"]; // collection expression
langs.Add("Go");
langs.Remove("F#");
Console.WriteLine($"{langs.Count} items, first: {langs[0]}");
var stock = new Dictionary<string, int>
{
["apple"] = 10,
["pear"] = 4,
};
stock["plum"] = 7; // adds or overwrites
if (stock.TryGetValue("pear", out var pears))
Console.WriteLine($"Pears in stock: {pears}");
foreach (var (fruit, qty) in stock)
Console.WriteLine($"{fruit} = {qty}");Reading a missing key with the indexer throws KeyNotFoundException, so use TryGetValue when a key may be absent. Other handy collections include HashSet<T> for unique items, Queue<T> for first-in-first-out, and Stack<T> for last-in-first-out. Fixed-size data fits naturally in an array such as int[].
With <Nullable>enable</Nullable> in the project file, the compiler tracks whether reference-type variables might be null. Plain string means "never null", while string? explicitly allows null. Using a maybe-null value without checking it produces a warning.
string? FindNickname(int userId) => userId == 1 ? "Sky" : null;
string? nick = FindNickname(2);
Console.WriteLine(nick?.Length); // null if nick is null, no exception
Console.WriteLine(nick ?? "(no nickname)"); // fallback value
nick ??= "Guest"; // assign only when null
if (nick is not null)
Console.WriteLine(nick.ToUpper()); // compiler knows nick is non-null here
int? age = null; // nullable value type
Console.WriteLine(age.HasValue ? age.Value : -1);?. skips the member access when the target is null, ?? supplies a default, and ??= fills in a value only if one is missing. When you are certain a value isn't null, the null-forgiving operator ! silences the warning, but it performs no runtime check, so use it sparingly.
init and required express how objects must be created.with.List<T> and Dictionary<TKey, TValue> cover most needs; use TryGetValue for safe lookups.string?, ?., and ??, catch many null bugs at compile time.
0 Kommentare
Anmelden · Melde dich an, um einen Kommentar zu schreiben.
Schreib den ersten Kommentar.