Mert Özen Her Satırda Daha İleri
Backend C# Sürümleri — #01

C# Sürümleri: 1.0'dan 10'a Dilin Kısa Tarihi

Mert Özen 7 Kas 2026 13 dk 8 görüntülenme
C# Sürümleri: 1.0'dan 10'a Dilin Kısa Tarihi

C# 1.0'dan C# 10'a her sürümün imza özellikleri, çıkış tarihleri ve .NET eşleşmesi; aynı kodu farklı LangVersion değerleriyle derleyip compiler'ın hangi özelliğin hangi sürümden geldiğini nasıl söylediğini görmek.

Sürüm notu: Bu yazı C# 1.0'dan C# 10'a dilin tarihini anlatır; örnekler .NET 10 SDK'da net6.0 hedefiyle ve farklı LangVersion değerleriyle derlenip denenmiştir.

Önce Tahmin Et

Aşağıdaki dosya bugün herhangi bir .NET 6 projesinde sorunsuz derlenir. Soru şu: bu kodu derleyebilen en eski C# sürümü hangisi? Proje dosyasında LangVersion'ı 9, 8, 7.3 ve 6 yapıp tek tek denedim. Cevaba geçmeden her satır için bir tahmin yürüt.

using System;
using System.Collections.Generic;

public class Book
{
    public string Title { get; set; }
    public decimal Price { get; set; }
}

public static class Program
{
    public static void Main()
    {
        var books = new List<Book>
        {
            new() { Title = "Clean Code", Price = 42.5m },
            new() { Title = "Refactoring", Price = 38m }
        };

        if (int.TryParse("3", out var stock))
            Console.WriteLine($"{books[0].Title}: {stock} in stock");

        string label = books[^1].Price switch
        {
            > 40m => "expensive",
            _ => "fine"
        };

        Dictionary<string, int> cache = null;
        cache ??= new Dictionary<string, int>();

        Console.WriteLine(label);
    }
}

Compiler Cevabı Biliyor

Cevap C# 9. LangVersion 9 iken kod derlenip şunu yazdırıyor:

Clean Code: 3 in stock
fine

Bir basamak aşağı inince compiler hangi satırın neden derlenmediğini tek tek söylüyor. C# 8 ile:

Program.cs(16,13): error CS8400: Feature 'target-typed object creation' is not available in C# 8.0. Please use language version 9.0 or greater.
Program.cs(17,13): error CS8400: Feature 'target-typed object creation' is not available in C# 8.0. Please use language version 9.0 or greater.
Program.cs(25,13): error CS8400: Feature 'relational pattern' is not available in C# 8.0. Please use language version 9.0 or greater.

C# 7.3 ile liste uzuyor:

Program.cs(16,13): error CS8370: Feature 'target-typed object creation' is not available in C# 7.3. Please use language version 9.0 or greater.
Program.cs(17,13): error CS8370: Feature 'target-typed object creation' is not available in C# 7.3. Please use language version 9.0 or greater.
Program.cs(23,40): error CS8370: Feature 'recursive patterns' is not available in C# 7.3. Please use language version 8.0 or greater.
Program.cs(23,30): error CS8370: Feature 'index operator' is not available in C# 7.3. Please use language version 8.0 or greater.
Program.cs(23,24): error CS8370: Feature 'index operator' is not available in C# 7.3. Please use language version 8.0 or greater.
Program.cs(25,13): error CS8370: Feature 'relational pattern' is not available in C# 7.3. Please use language version 9.0 or greater.
Program.cs(26,13): error CS8370: Feature 'recursive patterns' is not available in C# 7.3. Please use language version 8.0 or greater.
Program.cs(30,15): error CS8370: Feature 'coalescing assignment' is not available in C# 7.3. Please use language version 8.0 or greater.

C# 6'ya inince bunlara bir satır daha ekleniyor: Feature 'out variable declaration' is not available in C# 6. Please use language version 7.0 or greater. Yani tek dosyada dört farklı dönemin izi var. String interpolation C# 6'dan, out var C# 7.0'dan, ^1 index'i, switch expression ve ??= C# 8'den, new() ile > 40m gibi relational pattern de C# 9'dan geliyor.

Burada iki ayrıntı işine yarar. Birincisi, hata kodu sürüme göre değişir ama mesajın pattern'i hep aynıdır: C# 6 için CS8059, 7.3 için CS8370, 8.0 için CS8400, 9.0 için CS8773, 10.0 için CS8936. Mesaj hem özelliğin adını hem de gereken en düşük sürümü verir. İkincisi, compiler'ın kullandığı isim dokümantasyondakiyle her zaman aynı değildir. Switch expression hatası "recursive patterns" diye geliyor; C# 8'in dil tasarım belgelerinde bu özellik grubunun adı bu. Bu seri boyunca "bu satır hangi sürümden?" sorusunu hep bu yolla cevaplayacağız: tahmin etmek yerine compiler'a soracağız.

Dil Sürümünü Kim Belirler

Projende LangVersion yazmıyorsan dil sürümünü hedeflediğin framework belirler. Bunu görmenin en kısa yolu, dosyaya tek satır #error version yazıp derlemek. net6.0 hedefinde çıkan mesaj:

Program.cs(1,8): error CS8304: Compiler version: '5.9.0-1.26505.111 (a8dd0577c5c13f43a2f57fe33b4ff11b90f6371e)'. Language version: 10.0. Compiler path: '/usr/local/share/dotnet/sdk/10.0.402/Roslyn/bincore/Microsoft.CodeAnalysis.CSharp.dll'.

Compiler .NET 10 SDK'nın compiler'ı, ama dil sürümü 10.0. Aynı projede yalnızca TargetFramework'ü değiştirince sırasıyla net7.0 için 11.0, net8.0 için 12.0, net9.0 için 13.0, net10.0 için 14.0 geldi. Yani yeni bir SDK kurmak, eski projelerinin dil sürümünü kendiliğinden yükseltmez. Sürümü elle sabitlemek istersen proje dosyasına yazarsın:

<PropertyGroup>
  <TargetFramework>net6.0</TargetFramework>
  <LangVersion>7.3</LangVersion>
</PropertyGroup>

Bu seçim imkânı C# 7.1 ile geldi; dilin ilk ara sürümü oydu ve ara sürümler yüzünden hangi özelliği açacağını seçebilmen gerekiyordu. Microsoft'un önerisi net: LangVersion'ı latest yapma, framework'ün varsayılanından yeni bir sürüm de seçme, çünkü bu kombinasyon desteklenmiyor. Daha düşük bir sürüm seçmek ise yukarıdaki deneydeki gibi tamamen meşru.

2002–2005: C# 1.0 ve C# 2.0

01-csharp-timeline-tr.png

C# 1.0 Ocak 2002'de .NET Framework 1.0 ve Visual Studio .NET 2002 ile çıktı. Nisan 2003'te Visual Studio .NET 2003 ile küçük bir ara sürüm olan C# 1.2 geldi. İlk sürümün nasıl hissettirdiğini görmek için Bookstore'dan küçük bir parçayı LangVersion değeri ISO-1 iken derledim:

using System;
using System.Collections;

public delegate void StockChanged(string isbn, int stock);

public interface IPriced
{
    decimal Price { get; }
}

public enum Format { Paperback, Hardcover, Ebook }

public class Book : IPriced
{
    private decimal price;
    private int stock;
    public event StockChanged Changed;

    public Book(decimal price, int stock) { this.price = price; this.stock = stock; }

    public decimal Price { get { return price; } }

    public void Sell()
    {
        stock--;
        if (Changed != null) Changed("978-0132350884", stock);
    }
}

public class Program
{
    static void OnChanged(string isbn, int stock)
    {
        Console.WriteLine(isbn + ": " + stock);
    }

    public static void Main()
    {
        ArrayList books = new ArrayList();
        Book book = new Book(42.5m, 3);
        book.Changed += new StockChanged(OnChanged);
        books.Add(book);
        foreach (Book b in books)
        {
            b.Sell();
        }
        Book first = (Book)books[0];
        Console.WriteLine(first.Price.ToString(System.Globalization.CultureInfo.InvariantCulture) + " " + Format.Hardcover);
    }
}
978-0132350884: 2
42.5 Hardcover

Class, interface, enum, property, delegate, event ve foreach ilk günden beri var. Eksik olan şey tip güvenliği: ArrayList her şeyi object olarak tutuyor, okurken (Book) cast'i senin sorumluluğunda. İlk denemede ilginç bir hata da gördüm. SDK'nın her projeye otomatik eklediği küçük bir attribute dosyası global:: kullanıyor ve C# 1 bunu tanımıyor: Feature 'namespace alias qualifier' is not available in C# 1. Please use language version 2 or greater. Deneyi GenerateTargetFrameworkAttribute=false ile o dosyayı kapatarak yaptım.

Kasım 2005'teki C# 2.0, .NET Framework 2.0 ve Visual Studio 2005 ile geldi ve dilin yönünü değiştirdi. Başrolde generics var: List<Book> yazdığında C# 1 compiler'ı Feature 'generics' is not available in C# 1. Please use language version 2 or greater. diyor. Aynı sürüm başka parçalar da getirdi. Bu dosyayı yine C# 1 ile derledim:

using System;
using System.Collections;

public class Program
{
    static IEnumerable Countdown(int from)
    {
        for (int i = from; i > 0; i--)
            yield return i;
    }

    public static void Main()
    {
        int? stock = null;
        EventHandler log = delegate { Console.WriteLine("stock changed"); };
    }
}
Program.cs(9,13): error CS8022: Feature 'iterators' is not available in C# 1. Please use language version 2 or greater.
Program.cs(14,12): error CS8022: Feature 'nullable types' is not available in C# 1. Please use language version 2 or greater.
Program.cs(15,28): error CS8022: Feature 'anonymous methods' is not available in C# 1. Please use language version 2 or greater.

yield return ile iterator, int? ile nullable value type, isimsiz method olarak anonymous method. Partial type'lar ve static class'lar da C# 2 ile geldi. Generics'in neden bu kadar önemli olduğunu C# Koleksiyonlar ve Generics serisinin ilk yazısında ArrayList'ten List<T>'ye geçerek ölçüyoruz.

2007: C# 3.0 ve LINQ

C# 3.0 Kasım 2007'de .NET Framework 3.5 ve Visual Studio 2008 ile çıktı. Bu sürümün neredeyse her özelliği tek bir hedefe hizmet ediyordu: LINQ. Aşağıdaki dosyayı C# 2 ile derleyince ne kadar şeyin aynı anda geldiği görülüyor:

using System;
using System.Collections.Generic;
using System.Linq;

public class Book
{
    private string title;
    private decimal price;
    public string Title { get { return title; } set { title = value; } }
    public decimal Price { get { return price; } set { price = value; } }
}

public class Program
{
    static decimal Discount(decimal price, decimal rate) { return price * (1 - rate); }

    public static void Main()
    {
        List<Book> books = new List<Book>();
        var cheap = from b in books where b.Price < 40m select new { b.Title };
        Func<Book, bool> expensive = b => b.Price > 40m;
        Book first = new Book { Title = "Clean Code" };
        dynamic anything = 42;
        decimal total = Discount(price: 50m, rate: 0.2m);
    }
}
Program.cs(20,9): error CS8023: Feature 'implicitly typed local variable' is not available in C# 2. Please use language version 3 or greater.
Program.cs(20,21): error CS8023: Feature 'query expression' is not available in C# 2. Please use language version 3 or greater.
Program.cs(20,64): error CS8023: Feature 'anonymous types' is not available in C# 2. Please use language version 3 or greater.
Program.cs(21,40): error CS8023: Feature 'lambda expression' is not available in C# 2. Please use language version 3 or greater.
Program.cs(22,31): error CS8023: Feature 'object initializer' is not available in C# 2. Please use language version 3 or greater.
Program.cs(23,9): error CS0246: The type or namespace name 'dynamic' could not be found (are you missing a using directive or an assembly reference?)
Program.cs(24,34): error CS8023: Feature 'named argument' is not available in C# 2. Please use language version 4 or greater.
Program.cs(24,46): error CS8023: Feature 'named argument' is not available in C# 2. Please use language version 4 or greater.

Tek bir sorgu satırı için var, query expression ve anonymous type aynı anda gerekiyor. Lambda, object initializer, auto-implemented property ve extension method da aynı sürümden. Hepsi birbirine bağlı: Where bir extension method, aldığı filtre bir lambda, select new { b.Title } bir anonymous type. Expression tree'ler de C# 3 ile geldi; EF Core'un C# sorgusunu SQL'e çevirebilmesi onlar sayesinde. LINQ serisi tam olarak bu temelden başlıyor.

2010–2012: C# 4 ve C# 5

Son iki satır başka bir döneme ait. Aynı dosyayı C# 3 ile derleyince yalnızca onlar kalıyor:

Program.cs(23,9): error CS0246: The type or namespace name 'dynamic' could not be found (are you missing a using directive or an assembly reference?)
Program.cs(24,34): error CS8024: Feature 'named argument' is not available in C# 3. Please use language version 4 or greater.
Program.cs(24,46): error CS8024: Feature 'named argument' is not available in C# 3. Please use language version 4 or greater.

C# 4 Nisan 2010'da .NET Framework 4.0 ve Visual Studio 2010 ile geldi. dynamic, named argument'lar, optional parametreler ve generic covariance/contravariance (IEnumerable<out T>) bu sürümün. dynamic hatasının neden farklı göründüğüne dikkat et: dynamic bir contextual keyword. C# 3 compiler'ı onu keyword olarak tanımadığı için dynamic adında bir tip arıyor ve bulamıyor. Optional parametre için de mesaj net: Feature 'optional parameter' is not available in C# 3. Please use language version 4 or greater.

Ağustos 2012'de .NET Framework 4.5 ve Visual Studio 2012 ile gelen C# 5'in tek bir büyük hikâyesi var: async ve await. C# 4 ile bir async method derlemeye çalışınca Feature 'async function' is not available in C# 4. Please use language version 5 or greater. geliyor. Bugün yazdığımız backend kodunun büyük kısmı bu iki kelimenin üstünde duruyor; nasıl çalıştıklarını C# Asenkron Programlama serisinde açıyoruz.

2015–2018: C# 6 ve C# 7.x

Temmuz 2015'te Visual Studio 2015 ile çıkan C# 6, dil kadar compiler'ı da değiştirdi: compiler'ı bir servis gibi API olarak sunan Roslyn bu sürümle geldi. Dil tarafındaki yenilikler küçük ama her gün kullandığın türden: string interpolation ($"..."), nameof, null-conditional operatör (?.), expression-bodied member'lar, auto-property initializer'lar, catch ... when exception filter'ları, using static ve catch/finally içinde await.

Mart 2017'deki C# 7.0 ile dil, yılda birkaç kez ara sürüm çıkaran bir ritme geçti. 7.0 out variable'ları, tuple ve deconstruction'ı, is ile ilk pattern matching'i, local function'ları, discard'ları (_), ref return'leri ve throw expression'ları getirdi. Ağustos 2017'deki 7.1 async Main'i ve default literal'ini ekledi. Kasım 2017'deki 7.2 performans tarafına döndü: in parametreleri, readonly struct, ref struct ve private protected. Mayıs 2018'deki 7.3 ise unmanaged, System.Enum ve System.Delegate constraint'lerini ve tuple'larda == karşılaştırmasını getirdi. Span<T>'nin dil tarafındaki temeli olan ref struct'ı C# İleri Seviye serisinde göreceğiz.

2019–2021: C# 8, 9 ve 10

Eylül 2019'da .NET Core 3.0 ile gelen C# 8, .NET Core'u hedefleyen ilk sürüm oldu. Bazı özellikleri yeni runtime ya da kütüphane desteği istiyor. Default interface member'lar runtime desteğine ihtiyaç duyuyor; ^1 ve 1..3 da arkada System.Index ve System.Range tiplerini kullanıyor. Nullable reference type'lar, async stream'ler (IAsyncEnumerable<T> ve await foreach), switch expression ile property ve positional pattern'ler, ??= ve using var declaration'ları da bu sürümün.

Kasım 2020'de .NET 5 ile gelen C# 9, record'ları, init setter'ları, top-level statements'ı, relational ve logical pattern'leri (> 40m, and, or, not) ve target-typed new()'ü getirdi. O günden beri her Kasım'da bir .NET ve bir C# sürümü birlikte çıkıyor.

Kasım 2021'de .NET 6 LTS ile gelen C# 10, bu ailenin kemik serilerinin dayandığı sürüm. record struct, global using, file-scoped namespace (namespace Bookstore;), extended property pattern'ler ({ Author.Name: "Ada" }), lambda'ların natural type kazanması (var f = () => 1;), sabit interpolated string'ler ve CallerArgumentExpression bu sürümde. Tahmin sorusundaki dosyaya namespace Bookstore; satırını ekleyip C# 9 ile derleyince error CS8773: Feature 'file-scoped namespace' is not available in C# 9.0. Please use language version 10.0 or greater. geliyor. Generic attribute'lar ve interface'lerde static abstract member'lar C# 10'da yalnızca preview olarak vardı; gerçek hâlleriyle C# 11'de geldiler.

Sık Düşülen Tuzaklar

Dil sürümünü SDK sürümüyle karıştırmak. Makinende .NET 10 SDK olması, net6.0 hedefleyen projenin C# 14 ile derlendiği anlamına gelmez. Yukarıdaki #error version çıktısında compiler 10 SDK'dan, dil sürümü ise 10.0'dı. Emin değilsen o tek satırı ekleyip bak.

Her yeni syntax'ın sadece compiler'a bağlı olduğunu sanmak. Çoğu özellik gerçekten yalnızca compiler işi ve eski runtime'da da çalışır. Ama C# 8'de gördüğümüz gibi bazıları runtime ya da kütüphane tarafında bir tip veya yetenek ister. Bir sonraki yazıda generic math'in .NET 6'da neden derlenmediğini bu yüzden göreceğiz.

LangVersion'ı latest yapıp geçmek. Bu ayar, makinedeki SDK değiştikçe projenin dil sürümünü sessizce değiştirir; iki geliştiricinin aynı kodu farklı kurallarla derlemesine yol açar. Framework'ün varsayılanı neyse onu kullanmak en güvenli yol.

Hata mesajındaki özellik adını dokümantasyonda aynen aramak. "recursive patterns" ya da "coalescing assignment" gibi isimler compiler'ın iç isimleri; aradığını bulamazsan sürüm numarasıyla birlikte "What's new in C# 8" sayfasına bak.

Küçük Bir Deneme

★☆☆ Tahmin sorusundaki dosyayı kendi projene koy, LangVersion'ı 7.3 yap ve en az değişiklikle derlenir hâle getir. Kaç satıra dokunman gerekti?

★★☆ Boş bir console projesine #error version satırını ekle ve TargetFramework'ü net6.0, net8.0 ve net10.0 yaparak dil sürümünün nasıl değiştiğini kendi gözünle gör.

★★★ nameof, ?. ve string interpolation içeren tek bir method yaz ve LangVersion'ı 5 yaparak derle. Compiler hangi özellik adlarını kullanıyor, kaç hata alıyorsun?

C# 10'a kadar geldik ve kemik serilerin dili tam olarak bu. Bir sonraki yazıda ilk adımı atıyoruz: C# 11 ile gelen raw string literal'leri, required member'ları, list pattern'leri ve generic math'i, her birini önce C# 10'daki hâliyle yan yana koyarak görüyoruz.