C# Programlama

C# Programlama

C# 14 ve .NET 10 ekseninde tür sistemi, nesne yönelimli tasarım, genel türler, koleksiyonlar, LINQ, delegate/olaylar, async/await, eşzamanlılık, dosya ve ağ G/Ç, reflection, bellek ve performans konularını örneklerle ele alan ders notu.

C#; statik tür güvenliği, nesne yönelimli model, fonksiyonel ifade araçları, asenkron programlama ve çalışma zamanı hizmetlerini aynı dilde birleştirir. Dili doğru öğrenmek için üç katmanı ayırmak gerekir: C# kaynak dilidir, derleyici kaynak kodu ara dile ve üst veriye dönüştürür, .NET çalışma zamanı ise assembly'leri yükler ve kodu yürütür.

C# sözdizimi, dil semantiği ve çalışma zamanı maliyetleri birlikte değerlendirilmelidir. Temel dil yapıları ilerleyen bölümlerde genel türler, LINQ, eşzamanlılık, bellek, ağ ve reflection gibi konularla aynı model içinde birleşir.

1. C#, .NET ve CLR

Modern .NET, yalnız Windows'a bağlı eski .NET Framework modeli değildir. .NET 10 uzun süreli destek sürümüdür ve C# 14 ile birlikte kullanılabilir. CLR; assembly yükleme, JIT derleme, çöp toplama, istisna aktarımı, iş parçacığı yönetimi ve tür güvenliği gibi çalışma zamanı hizmetlerini sağlar.

C# kaynak kodu doğrudan işlemci komutlarından oluşmaz. Derleme sonucunda assembly içinde Common Intermediate Language ve üst veri bulunur. Çalışma zamanı ihtiyaç duyulan metotları yerel makine koduna dönüştürebilir. Native AOT ise uygun uygulamalarda yürütülebilir kodu önceden üretme seçeneği sunar.

using System;

public static class Program
{
    public static void Main()
    {
        Console.WriteLine($".NET: {Environment.Version}");
        Console.WriteLine($"64 bit süreç: {Environment.Is64BitProcess}");
    }
}

Managed kod bellek güvenliği ve çalışma zamanı hizmetlerinden yararlanır. Bununla birlikte dosya tanıtıcısı, socket, veritabanı bağlantısı veya native handle gibi dış kaynakların ömrünü çöp toplayıcıya bırakmak doğru değildir.

2. Derleme, SDK ve proje modeli

Güncel komut satırı akışı dotnet CLI üzerine kuruludur:

dotnet new console -n Ornek
dotnet build Ornek
dotnet run --project Ornek

SDK hedef framework, dil sürümü, nullable analiz ve optimizasyon gibi seçenekleri proje dosyasından yönetir. Üretimde kullanılan dil özelliği, yalnız geliştiricinin IDE sürümüne değil teslim hattındaki SDK ve derleyici sürümüne de bağlıdır.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net10.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>
</Project>

3. Tür sistemi: değer ve referans semantiği

C# tür sistemi, yalnız değişkenin hangi değeri tutacağını değil kopyalama, null olabilme, boxing ve bellek davranışını da belirler. int, double, bool, char, decimal ve kullanıcı tanımlı struct türleri değer türleridir. Sınıflar, diziler, delegate'ler ve string referans türüdür.

decimal, ondalık finansal hesaplarda ikili kayan noktanın temsil hatalarını azaltmak için tasarlanmıştır. double ise bilimsel ve genel sayısal hesaplamalarda daha yaygın ve hızlıdır.

decimal net = 1250.75m;
decimal taxRate = 0.20m;
decimal total = net * (1m + taxRate);

int count = 42;
long widened = count;

checked
{
    int narrowed = checked((int)3_000_000_000L);
    Console.WriteLine(narrowed);
}

Son örnek OverflowException üretir. checked taşmanın sessizce sarmalanmasının kabul edilmediği hesaplarda önemlidir.

4. Nullable türler ve null güvenliği

Nullable reference types çalışma zamanı özelliği değil, derleyici tarafından yapılan statik analizdir. string null olmaması beklenen, string? ise null olabilen referansı ifade eder. Bu ayrım API sözleşmesini görünür kılar.

static int LengthOrZero(string? value)
{
    return value?.Length ?? 0;
}

string? input = Console.ReadLine();
Console.WriteLine(LengthOrZero(input));

Null-forgiving ! operatörü null kontrolü yapmaz; yalnız derleyici uyarısını bastırır. Bu nedenle gerçek bir invariant tarafından desteklenmediği sürece kullanılmamalıdır.

5. Operatörler, kısa devre ve bit işlemleri

&& ve || kısa devrelidir; sağ taraf yalnız gerektiğinde değerlendirilir. &, |, ^, ~, << ve >> bit düzeyinde protokol alanı, maske ve düşük seviye veri işleme gibi durumlarda kullanılır.

const byte Valid = 0b0000_0001;
const byte Encrypted = 0b0000_0010;

byte flags = Valid | Encrypted;

bool valid = (flags & Valid) != 0;
bool encrypted = (flags & Encrypted) != 0;

Console.WriteLine($"{valid} {encrypted}");

Bit alanları için anlamlı sabitler veya [Flags] enum kullanmak çıplak sayılardan daha güvenlidir.

6. Karar yapıları ve örüntü eşleme

Klasik if ve switch hâlâ temel yapılardır; modern C# bunları örüntü eşleme ile genişletir. Tür, sabit, ilişki ve property pattern'leri karar mantığını daha bildirisel yazmayı sağlar.

static string Classify(int latencyMs) => latencyMs switch
{
    < 0 => throw new ArgumentOutOfRangeException(nameof(latencyMs)),
    < 20 => "çok düşük",
    < 100 => "düşük",
    < 500 => "orta",
    _ => "yüksek"
};

Console.WriteLine(Classify(87));

Örüntü eşleme, uzun if-else zincirini kısaltabilir; ancak iş kuralı büyüdükçe karar tablosu veya ayrı politika nesnesi daha sürdürülebilir olabilir.

7. Döngüler ve enumerator modeli

for, sayaç veya indeks gerektiğinde; foreach, koleksiyonun elemanları üzerinde ilerlerken; while koşul başta, do-while koşul sonda değerlendirildiğinde uygundur.

int[] samples = [12, 18, 21, 17, 25];

long sum = 0;
foreach (int sample in samples)
{
    sum += sample;
}

double average = (double)sum / samples.Length;
Console.WriteLine(average);

foreach sözdizimi IEnumerable<T> ile sınırlı değildir; uygun GetEnumerator pattern'ini sağlayan türler de kullanılabilir.

8. Metotlar, parametre aktarımı ve ref ailesi

C# varsayılan olarak argümanı değer olarak aktarır. Referans türünde bu değer nesnenin kendisi değil referansın kopyasıdır. ref, out ve in parametre aktarım semantiğini değiştirir.

static bool TryNormalize(int value, out double normalized)
{
    if (value is < 0 or > 100)
    {
        normalized = 0;
        return false;
    }

    normalized = value / 100.0;
    return true;
}

if (TryNormalize(73, out double result))
{
    Console.WriteLine(result);
}

Çok sayıda değer döndürmek için out yerine tuple veya küçük bir record/struct çoğu zaman daha okunabilirdir. ref özellikle kopyalama maliyeti yüksek değer türlerinde ve düşük seviyeli API'lerde anlamlıdır.

9. Sınıflar, kapsülleme ve nesne ömrü

Alanları doğrudan herkese açmak yerine invariant'ı koruyan property ve metotlar tercih edilir. Kurucu, nesnenin geçerli başlangıç durumunu kurmalıdır.

public sealed class RateLimiter
{
    private int _limit;

    public RateLimiter(int limit)
    {
        Limit = limit;
    }

    public int Limit
    {
        get => _limit;
        set => _limit = value > 0
            ? value
            : throw new ArgumentOutOfRangeException(nameof(value));
    }
}

C# 14 field anahtar sözcüğüyle compiler-generated backing field üzerinde doğrulama yapılmasını kolaylaştırır:

public sealed class Endpoint
{
    public string Name
    {
        get;
        set => field = string.IsNullOrWhiteSpace(value)
            ? throw new ArgumentException("Ad boş olamaz.", nameof(value))
            : value.Trim();
    } = "default";
}

Bu ikinci örnek C# 14 gerektirir.

10. Record, equality ve immutability

Referans eşitliği ile değer eşitliği farklıdır. Normal sınıflarda == varsayılan olarak çoğunlukla referans kimliğiyle ilgilidir; record türleri ise veri taşıyan modeller için değer-temelli equality üretir.

public sealed record SensorReading(string Source, long Sequence, double Value);

SensorReading a = new("A1", 7, 42.5);
SensorReading b = new("A1", 7, 42.5);

Console.WriteLine(a == b);

SensorReading c = a with { Sequence = 8 };
Console.WriteLine(c);

Kendi sınıfınızda equality tanımlıyorsanız Equals ve GetHashCode sözleşmeleri birlikte düşünülmelidir. Hash tabanlı koleksiyonlarda değişebilir alanlardan hash üretmek ciddi hatalara yol açabilir.

11. Kalıtım, composition ve polymorphism

Kalıtım bir "is-a" ilişkisini temsil etmelidir. Yalnız kod tekrarını azaltmak için hiyerarşi kurmak kırılgan tasarıma neden olabilir. Davranış değişimini composition ve interface ile modellemek çoğu durumda daha esnektir.

public interface IFormatter
{
    string Format(double value);
}

public sealed class FixedFormatter : IFormatter
{
    public string Format(double value) => value.ToString("F2");
}

public sealed class SampleWriter
{
    private readonly IFormatter _formatter;

    public SampleWriter(IFormatter formatter)
    {
        _formatter = formatter;
    }

    public string Write(double value) => _formatter.Format(value);
}

virtual/override dinamik dispatch sağlar. sealed hem tipin hem override zincirinin sınırlandırılmasında kullanılabilir.

12. Interface, struct, enum ve boxing

struct küçük, değer semantiği taşıyan ve mümkünse immutable veri modelleri için uygundur. Büyük mutable struct'lar kopyalama maliyeti ve şaşırtıcı semantik üretir. readonly record struct bu tür modeller için güçlü bir seçenektir.

public enum PacketState : byte
{
    Unknown,
    Ready,
    Sent,
    Failed
}

public readonly record struct Coordinate(double X, double Y);

Coordinate p = new(12.5, 8.25);
PacketState state = PacketState.Ready;
Console.WriteLine($"{p} {state}");

Bir değer türü object veya uyguladığı interface türüne dönüştürüldüğünde boxing oluşabilir. Sık çalışan sıcak yollarda gereksiz kutulama kaynaklı ayırma ve GC baskısı yaratabilir.

13. Diziler, Span<T> ve bellek görünümü

Diziler sabit uzunluklu referans türleridir. Çok boyutlu gerçek diziler T[,], jagged diziler ise dizi dizisi T[][] biçimindedir. Performans duyarlı kodlarda Span<T> ve ReadOnlySpan<T> mevcut belleğin yeni allocation yapmadan dilimlenmesini sağlar.

static int SumPositive(ReadOnlySpan<int> values)
{
    int sum = 0;

    foreach (int value in values)
    {
        if (value > 0)
        {
            sum += value;
        }
    }

    return sum;
}

int[] values = [4, -2, 7, 3];
Console.WriteLine(SumPositive(values.AsSpan(1, 3)));

Span<T> bir ref struct olduğundan heap'e taşınamaz ve yaşam süresi kuralları daha katıdır. Bu kısıtlar güvenli düşük-allocation kod üretmek içindir.

14. String, biçimlendirme ve kültür

string immutable'dır. Çok sayıda parçayı döngü içinde birleştirmek için StringBuilder, formatlama için interpolation veya uygun format API'leri kullanılabilir.

using System.Globalization;

double value = 12345.6789;

Console.WriteLine(value.ToString("F2", CultureInfo.InvariantCulture));
Console.WriteLine($"{value:N2}");

Makine-makine protokollerinde kültürden bağımsız biçim (InvariantCulture) tercih edilmelidir. Kullanıcı arayüzünde ise kullanıcının kültürü korunmalıdır.

15. Generics ve koleksiyonlar

Eski non-generic ArrayList ve Hashtable yerine List<T>, Dictionary<TKey,TValue>, HashSet<T>, Queue<T> ve Stack<T> gibi generic koleksiyonlar tercih edilir. Böylece cast ihtiyacı ve kutulama (boxing) riski azalır.

var latest = new Dictionary<string, double>(StringComparer.OrdinalIgnoreCase)
{
    ["A1"] = 12.4,
    ["B7"] = 19.8
};

latest["A1"] = 13.1;

if (latest.TryGetValue("a1", out double current))
{
    Console.WriteLine(current);
}

Koleksiyon seçimi erişim biçimine göre yapılmalıdır. List<T> indeks ve ardışık tarama, Dictionary<TKey,TValue> anahtar erişimi, HashSet<T> üyelik testi için uygundur.

16. LINQ: sorgu semantiği ve ertelenmiş yürütme

LINQ, koleksiyon işleme için ortak bir sorgu modeli sunar. IEnumerable<T> üzerindeki birçok operatör ertelenmiş yürütülür; sonuç enumerasyon başladığında üretilir.

int[] samples = [4, 11, 7, 21, 9, 16];

var query = samples
    .Where(static x => x >= 10)
    .OrderBy(static x => x)
    .Select(static x => x * 2);

foreach (int item in query)
{
    Console.WriteLine(item);
}

ToArray, ToList, Count gibi terminal işlemler yürütmeyi tetikleyebilir. IQueryable<T> ise expression tree üzerinden sorguyu başka bir sisteme çevirebilir; bu nedenle bellek içi LINQ ile veritabanı LINQ'u aynı maliyet modeline sahip değildir.

17. Delegate, lambda ve event

Delegate, imzalı çağrılabilir referanstır. Lambda, delegate veya expression tree üretmek için kısa sözdizimi sağlar. Event ise yayıncının abonelik kontrolünü koruduğu bildirim modelidir.

public sealed class ThresholdMonitor
{
    public event EventHandler<double>? Exceeded;

    public void Observe(double value)
    {
        if (value > 80)
        {
            Exceeded?.Invoke(this, value);
        }
    }
}

var monitor = new ThresholdMonitor();
monitor.Exceeded += static (_, value) => Console.WriteLine($"Eşik aşıldı: {value}");
monitor.Observe(91.5);

Event aboneliklerinde yayıncı uzun ömürlü, abone kısa ömürlü ise abonelik kaldırılmadığında bellek tutulması oluşabilir.

18. İstisna yönetimi

İstisna, beklenmeyen veya normal dönüş değeriyle temsil edilmesi uygun olmayan hata durumları içindir. Akış kontrolü amacıyla sürekli istisna üretmek pahalıdır.

static int ParsePort(string text)
{
    if (!int.TryParse(text, out int port) || port is < 1 or > 65535)
    {
        throw new ArgumentOutOfRangeException(nameof(text), "Geçerli bir TCP/UDP portu girilmelidir.");
    }

    return port;
}

try
{
    Console.WriteLine(ParsePort("70000"));
}
catch (ArgumentOutOfRangeException ex)
{
    Console.WriteLine(ex.Message);
}

Exception yeniden fırlatılırken throw; mevcut stack trace'i korur. throw ex; ise tanılama bilgisini bozabilir. finally kaynak temizleme için kullanılabilir; fakat using/await using çoğu sahiplik senaryosunu daha açık ifade eder.

19. Kaynak sahipliği: IDisposable ve IAsyncDisposable

GC yönetilen belleği geri kazanır; dış kaynakların zamanında bırakılmasını garanti etmez. Deterministik temizlik için IDisposable kullanılır.

using FileStream stream = File.OpenRead("data.bin");

byte[] buffer = new byte[4096];
int read = stream.Read(buffer, 0, buffer.Length);

Console.WriteLine(read);

Finalizer son çaredir; normal kaynak yönetiminin yerine geçmez. Native handle sahipliğinde SafeHandle tabanlı yaklaşım çoğu zaman doğrudan finalizer yazmaktan güvenlidir.

20. Dosya G/Ç ve modern asenkron API

FileStream, StreamReader, StreamWriter, BinaryReader, BinaryWriter hâlâ geçerlidir. Küçük ve basit işlemler için File sınıfı daha az kod gerektirir.

static async Task<long> CountLinesAsync(string path, CancellationToken cancellationToken)
{
    long count = 0;

    await using FileStream stream = new(
        path,
        FileMode.Open,
        FileAccess.Read,
        FileShare.Read,
        bufferSize: 64 * 1024,
        options: FileOptions.Asynchronous | FileOptions.SequentialScan);

    using var reader = new StreamReader(stream);

    while (await reader.ReadLineAsync(cancellationToken) is not null)
    {
        count++;
    }

    return count;
}

Büyük dosyada tüm içeriği ReadAllText ile belleğe almak yerine akış bazlı işleme daha öngörülebilir bellek kullanımı sağlar.

21. async/await, Task ve cancellation

Asenkronluk paralellik değildir. I/O bekleyen işlemde iş parçacığını bloke etmeden devam edebilmek için kullanılır. CPU yoğun işte ise paralellik ayrı bir karardır.

static async Task<string[]> FetchAllAsync(HttpClient client, Uri[] addresses, CancellationToken cancellationToken)
{
    Task<string>[] tasks = addresses
        .Select(uri => client.GetStringAsync(uri, cancellationToken))
        .ToArray();

    return await Task.WhenAll(tasks);
}

Sınırsız paralel istek üretmek throughput'u artırmak yerine socket, uzak servis veya bellek sınırlarını aşabilir. Concurrency sınırı semafor, kanal veya iş kuyruğu ile yönetilmelidir.

22. Eşzamanlılık ve paylaşılan durum

Paylaşılan mutable state yarış durumlarının ana kaynağıdır. lock kritik bölgeyi tek yürütücüye indirger. Atomik sayaçlar için Interlocked, producer-consumer akışları için Channel<T> gibi daha özel araçlar vardır.

public sealed class Counter
{
    private long _value;

    public long Increment()
    {
        return Interlocked.Increment(ref _value);
    }

    public long Value => Interlocked.Read(ref _value);
}

Eski Thread.Suspend, Thread.Resume ve Thread.Abort desenleri modern .NET için doğru eşzamanlılık modeli değildir. İptal CancellationToken, bekleme Task, coordination ise uygun concurrent primitive'lerle yapılmalıdır.

23. HTTP ve ağ istemcisi

Eski WebRequest/WebClient merkezli kod yerine güncel istemci API'si HttpClient'tır. İstemci yaşam döngüsü önemlidir; her istek için yeni istemci üretmek bağlantı havuzunun avantajlarını yok edebilir.

using var client = new HttpClient
{
    Timeout = TimeSpan.FromSeconds(5)
};

using HttpResponseMessage response = await client.GetAsync(
    "https://example.com/",
    HttpCompletionOption.ResponseHeadersRead);

response.EnsureSuccessStatusCode();

await using Stream body = await response.Content.ReadAsStreamAsync();
Console.WriteLine(response.StatusCode);

Sunucu uygulamalarında IHttpClientFactory gibi yönetilen yaşam döngüsü mekanizmaları tercih edilebilir.

24. Reflection, attribute ve üst veri

Reflection tip ve üyeleri çalışma zamanında incelemeyi sağlar. Güçlüdür ancak doğrudan çağrıdan daha maliyetlidir ve trimming/Native AOT senaryolarında dinamik erişimler ayrıca düşünülmelidir.

[AttributeUsage(AttributeTargets.Class)]
public sealed class HandlerAttribute(string name) : Attribute
{
    public string Name { get; } = name;
}

[Handler("telemetry")]
public sealed class TelemetryHandler;

Type type = typeof(TelemetryHandler);
HandlerAttribute? attribute = type
    .GetCustomAttributes(typeof(HandlerAttribute), inherit: false)
    .Cast<HandlerAttribute>()
    .SingleOrDefault();

Console.WriteLine(attribute?.Name);

Kaynak üreteçleri veya compile-time registration, yüksek performans ve AOT uyumluluğu gereken durumlarda reflection'a alternatif olabilir.

25. Unsafe kod, pointer ve stackalloc

C# gerektiğinde unsafe kod ve pointer kullanımına izin verir. Bu alan güvenli managed kodun garanti setini daraltır; yalnız ölçülmüş gereksinim olduğunda kullanılmalıdır.

static int SumStackAllocated()
{
    Span<int> values = stackalloc int[4] { 3, 5, 7, 11 };
    int sum = 0;

    foreach (int value in values)
    {
        sum += value;
    }

    return sum;
}

Console.WriteLine(SumStackAllocated());

Bu örnek pointer kullanmadan stack allocation gösterir. unsafe pointer erişimi native birlikte çalışabilirlik veya çok özel performans yollarında gerekebilir.

26. Operatör aşırı yükleme, indexer ve dönüşümler

Operatör aşırı yükleme, yalnız işlemin anlamı domain içinde doğal olduğunda kullanılmalıdır. Sürpriz semantik okunabilirliği azaltır.

public readonly record struct Vector2(double X, double Y)
{
    public static Vector2 operator +(Vector2 left, Vector2 right)
        => new(left.X + right.X, left.Y + right.Y);

    public double this[int index] => index switch
    {
        0 => X,
        1 => Y,
        _ => throw new IndexOutOfRangeException()
    };
}

Vector2 v = new(1, 2) + new Vector2(3, 4);
Console.WriteLine($"{v[0]}, {v[1]}");

Implicit dönüşümler veri kaybı veya şaşırtıcı maliyet üretmemelidir. Riskli dönüşümler explicit olmalıdır.

27. XML documentation ve API sözleşmesi

Public API'lerde XML documentation IDE desteği ve dokümantasyon üretimi için kullanılabilir. Ancak yorum, kodun zaten söylediğini tekrar etmek yerine sözleşme, sınır ve hata davranışını açıklamalıdır.

/// <summary>0 ile 1 arasında normalize edilmiş oran döndürür.</summary>
/// <exception cref="ArgumentOutOfRangeException">Değer 0..100 aralığında değilse.</exception>
public static double NormalizePercent(int value)
{
    if (value is < 0 or > 100)
    {
        throw new ArgumentOutOfRangeException(nameof(value));
    }

    return value / 100.0;
}

28. Basit bir recursive-descent ayrıştırıcı

Dil işleme, yalnız derleyici teorisine ait değildir. Küçük kural dilleri, filtre ifadeleri ve yapılandırma sözdizimleri recursive-descent parser ile okunabilir.

Aşağıdaki örnek toplama ve çarpmanın önceliğini koruyan küçük bir aritmetik ayrıştırıcıdır:

public sealed class ExpressionParser
{
    private readonly string _text;
    private int _index;

    public ExpressionParser(string text)
    {
        _text = text;
    }

    public double Parse()
    {
        double value = ParseExpression();
        SkipSpaces();

        if (_index != _text.Length)
        {
            throw new FormatException($"Beklenmeyen karakter: {_text[_index]}");
        }

        return value;
    }

    private double ParseExpression()
    {
        double value = ParseTerm();

        while (true)
        {
            SkipSpaces();

            if (Match('+'))
            {
                value += ParseTerm();
            }
            else if (Match('-'))
            {
                value -= ParseTerm();
            }
            else
            {
                return value;
            }
        }
    }

    private double ParseTerm()
    {
        double value = ParseNumber();

        while (true)
        {
            SkipSpaces();

            if (Match('*'))
            {
                value *= ParseNumber();
            }
            else if (Match('/'))
            {
                value /= ParseNumber();
            }
            else
            {
                return value;
            }
        }
    }

    private double ParseNumber()
    {
        SkipSpaces();
        int start = _index;

        while (_index < _text.Length &&
               (char.IsDigit(_text[_index]) || _text[_index] == '.'))
        {
            _index++;
        }

        if (start == _index ||
            !double.TryParse(
                _text.AsSpan(start, _index - start),
                System.Globalization.NumberStyles.Float,
                System.Globalization.CultureInfo.InvariantCulture,
                out double value))
        {
            throw new FormatException("Sayı bekleniyordu.");
        }

        return value;
    }

    private bool Match(char expected)
    {
        if (_index >= _text.Length || _text[_index] != expected)
        {
            return false;
        }

        _index++;
        return true;
    }

    private void SkipSpaces()
    {
        while (_index < _text.Length && char.IsWhiteSpace(_text[_index]))
        {
            _index++;
        }
    }
}

Console.WriteLine(new ExpressionParser("2 + 3 * 4").Parse());

Gerçek bir parser için parantez, unary operatör, hata konumu, tokenization ve limitler ayrıca ele alınmalıdır.

29. Masaüstü, web ve uygulama katmanları

Windows Forms ve WPF, Windows masaüstünde yaşamaya devam eder. ASP.NET Core web/API uygulamalarının ana .NET çatısıdır. .NET MAUI mobil ve masaüstü istemciler için ortak bir uygulama modeli sunar. Dil bilgisi bu UI veya web çatılarına bağlı değildir; aynı C# tür sistemi ve çalışma zamanı ilkeleri farklı uygulama modellerinde kullanılır.

UI kodunda iş kurallarını event handler içine yığmak yerine domain ve servis katmanlarını ayrıştırmak test edilebilirliği artırır.

30. C# 14 ve .NET 10 ile güncel dil çizgisi

C# 14; extension members, null-conditional assignment, field backed property, daha güçlü Span<T> dönüşümleri, genişletilmiş partial üyeler ve kullanıcı tanımlı compound assignment gibi yenilikler getirir. Bu özellikler temel C# bilgisinin yerine geçmez; var olan tür, allocation, dispatch ve lifetime kuralları üzerinde çalışır.

public sealed class Job
{
    public string? Result { get; set; }
}

Job? current = new();
current?.Result = "done";

Console.WriteLine(current?.Result);

Null-conditional assignment C# 14 özelliğidir.

.NET 10 çalışma zamanı JIT inlining, devirtualization, stack allocation, Native AOT ve kod üretimi tarafında geliştirmeler içerir. Performans davranışı yine ölçülmelidir; yeni runtime sürümü her iş yükünde otomatik ve aynı oranda hızlanma anlamına gelmez.

31. Eski C#/.NET kodunu güncellerken

Eski kaynakların büyük bölümü dilin temel semantiği açısından hâlâ öğreticidir; fakat API seçimi ve çalışma zamanı varsayımları güncellenmelidir.

  • ArrayList yerine List<T>, Hashtable yerine Dictionary<TKey,TValue> kullanılır.
  • WebRequest/WebClient yerine HttpClient tercih edilir.
  • Thread.Abort, Suspend ve Resume yerine cooperative cancellation ve task tabanlı model kullanılır.
  • Finalizer ile doğrudan kaynak yönetimi yerine IDisposable, IAsyncDisposable ve gerekirse SafeHandle kullanılır.
  • String birleştirme, I/O ve collection kodunda allocation davranışı ayrıca değerlendirilir.
  • Nullable reference types ve analyzers yeni kodda sözleşme hatalarını erken yakalamaya yardımcı olur.
  • Reflection yoğun mimariler trimming ve Native AOT ile birlikte yeniden değerlendirilir.
  • Unsafe kod, "daha hızlıdır" varsayımıyla değil ölçülmüş gereksinim ve sınırlandırılmış kapsamla kullanılır.

32. Performans ve üretim davranışı

C# uygulamasının maliyeti yalnız algoritmanın Big-O sınıfından oluşmaz. Allocation oranı, GC nesilleri, boxing, interface dispatch, closure üretimi, async state machine, contention, syscalls, I/O batching ve veri yerleşimi gerçek gecikmeyi etkiler.

Yüksek trafikli sistemlerde şu sorular yararlıdır:

  • Sıcak yol her çağrıda allocation yapıyor mu?
  • Koleksiyon kapasitesi öngörülebiliyor mu?
  • LINQ zinciri okunabilirlik kazandırırken gereksiz enumerator veya ara koleksiyon oluşturuyor mu?
  • Task üretimi gerçekten asenkron bekleme için mi?
  • Paylaşılan lock altında I/O yapılıyor mu?
  • Cancellation ve timeout katmanlar arasında taşınıyor mu?
  • Serialization sırasında gereksiz ara string veya byte[] üretiliyor mu?
  • Ölçüm BenchmarkDotNet, profiler veya production-benzeri yük ile doğrulanıyor mu?

Dil özelliği seçimi, okunabilirlik ve bakım maliyeti kadar allocation, latency ve concurrency davranışıyla da değerlendirilmelidir.

İlgili dersler

C#'ın diğer dillerle tür sistemi ve çalışma zamanı açısından karşılaştırılması için Programlama Dilleri, düşük seviye bellek modeli için C Programlama ve C++ Programlama, JVM karşılaştırması için Java Programlama, yazılım tasarımı için Yazılım Mühendisliği notları birlikte okunabilir.

Kaynaklar

  • Microsoft. C# Guide. https://learn.microsoft.com/dotnet/csharp/
  • Microsoft. C# Language Reference. https://learn.microsoft.com/dotnet/csharp/language-reference/
  • Microsoft. What's new in C# 14. https://learn.microsoft.com/dotnet/csharp/whats-new/csharp-14
  • Microsoft. What's new in .NET 10. https://learn.microsoft.com/dotnet/core/whats-new/dotnet-10/
  • Microsoft. .NET API Browser. https://learn.microsoft.com/dotnet/api/
  • ECMA International. ECMA-334: C# Language Specification, 7th Edition. https://ecma-international.org/publications-and-standards/standards/ecma-334/
  • ECMA International. ECMA-335: Common Language Infrastructure. https://ecma-international.org/publications-and-standards/standards/ecma-335/
  • Jeffrey Richter. CLR via C#, 4th Edition. Microsoft Press, 2012.
İçindekiler
Bu sayfanın QR kodu