Mert Özen Her Satırda Daha İleri
Backend C# Temelleri — #02

C# Kurulum ve İlk Program: dotnet new console ve Proje Dosyası

Mert Özen 7 Kas 2026 8 dk 1 görüntülenme
C# Kurulum ve İlk Program: dotnet new console ve Proje Dosyası

.NET 10 SDK'yı kurmak, dotnet new console ile ilk proje, Main'i compiler'ın ürettiği top-level statements, csproj'daki TargetFramework, ImplicitUsings, Nullable ve LangVersion, dotnet run ve build, IDE seçimi ve ilk derleyici hatasını okumak.

Sürüm notu: Bu yazıdaki kodlar C# 10 ve .NET 6 API'leriyle yazılmıştır; .NET 10 SDK ile derlenip denenmiştir.

Önce Tahmin Et

Yeni bir C# projesi oluşturduğunda Program.cs dosyasının içinde tam olarak şu tek satır var:

Console.WriteLine("Hello, World!");

Başka hiçbir şey yok. Ne bir class, ne bir Main method'u, ne de Console'un nereden geldiğini söyleyen bir using satırı. Eski bir C# kitabı okuduysan programın static void Main ile başlaması gerektiğini biliyorsun. Bu dosya derlenir mi? Derlenirse Main nerede?

Derlenir ve ekrana Hello, World! yazar. Eksik görünen her parçayı compiler ve proje ayarları senin yerine tamamlıyor. Bu yazıda .NET SDK'yı kuracak, ilk projeyi oluşturacak, o görünmeyen parçaların nereden geldiğini bulacak ve ilk derleyici hatamızı bilerek alacağız.

SDK'yı Kurmak

Önceki yazıda gördüğümüz gibi kod yazmak için runtime değil SDK kurarsın. Bugün yeni bir kurulum için doğru seçim .NET 10 SDK: LTS sürümü ve 14 Kasım 2028'e kadar destekleniyor. Resmi indirme sayfası dotnet.microsoft.com; her işletim sistemi için hazır kurulum paketi orada. Paket yöneticisi kullanmayı seviyorsan Windows'ta winget install Microsoft.DotNet.SDK.10, macOS'ta Homebrew ile brew install --cask dotnet-sdk iş görür. Linux'ta dağıtımının paket yöneticisini ya da Microsoft'un kurulum betiğini kullanabilirsin.

Kurulum bittikten sonra yeni bir terminal aç ve SDK'nın yerinde olduğunu kontrol et:

$ dotnet --version
10.0.402

Sende sürüm numarasının son kısmı farklı olabilir; önemli olan 10 ile başlaması. Komut bulunamadı hatası alırsan terminali kapatıp yeniden aç, kurulum PATH'i güncellemiş ama açık terminal bunu henüz görmemiş olabilir.

İlk Proje: dotnet new console

dotnet komutu, SDK'nın her işi yaptığı tek kapı. Yeni bir console uygulaması için:

$ dotnet new console -o Bookstore
The template "Console App" was created successfully.

Processing post-creation actions...
Restoring .../Bookstore/Bookstore.csproj:
Restore succeeded.

new console "console template'inden yeni bir proje oluştur" demek, -o Bookstore ise projeyi Bookstore adında yeni bir klasöre koyar. Proje adı da klasör adından gelir. Klasörde üç şey oluşur: Program.cs, Bookstore.csproj ve obj klasörü. Son satırdaki restore, projenin ihtiyaç duyduğu paketlerin hazırlanması; obj klasörü bu işin ve derlemenin ara dosyalarını tutar.

Klasöre girip uygulamayı çalıştır:

$ cd Bookstore
$ dotnet run
Hello, World!

dotnet run projeyi derler ve hemen çalıştırır. Geliştirme sırasında en sık kullanacağın komut bu.

Top-Level Statements: Main Nerede?

Önce Tahmin Et'in cevabına gelelim. Program.cs'teki satırlar top-level statements: bir class ya da method içinde değil, dosyanın en üst seviyesinde yazılmış statement'lar. C# 9 ile gelen bu özellik .NET 6'dan beri console template'inin varsayılanı. Compiler bu satırları görünce senin için bir Program class'ı ve onun içinde bir giriş method'u üretir. Derlenen .dll'i ILSpy ile C#'a geri çevirdiğimde şunu gördüm:

[CompilerGenerated]
internal class Program
{
	private static void Main(string[] args)
	{
		Console.WriteLine("Hello, World!");
	}
}

ILSpy method'u okunaklı olsun diye Main diye gösteriyor; IL'deki gerçek adı <Main>$. Bu ad C# kodunda yazılamayan karakterler içerir, böylece senin yazacağın hiçbir method'la çakışmaz. Aynı template'i klasik hâliyle görmek istersen dotnet new console --use-program-main komutu şunu üretir:

namespace Classic;

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Hello, World!");
    }
}

İkisi de aynı programa dönüşür. Bu seride örnekleri kısa tutmak için top-level statements kullanıyoruz. Üretilen method'un bir string[] args parametresi olduğuna dikkat et; top-level kodda bu parametreye doğrudan args adıyla ulaşırsın:

string name = args.Length > 0 ? args[0] : "reader";

Console.WriteLine($"Hello, {name}! Welcome to the Bookstore.");
Console.WriteLine($"Today is {DateTime.Now.DayOfWeek}.");
$ dotnet run
Hello, reader! Welcome to the Bookstore.
Today is Saturday.
$ dotnet run -- Ada
Hello, Ada! Welcome to the Bookstore.
Today is Saturday.

-- işaretinden sonrası dotnet run'a değil senin programına gider. ?: operatörünü ve $"..." ile yazılan string'leri ilerleyen yazılarda ayrıntılı göreceğiz; şimdilik "argüman varsa ilkini al, yoksa reader yaz" diye okuman yeterli.

Top-level statements'ın iki kuralı var. Bir projede sadece bir dosya top-level kod içerebilir, çünkü programın tek bir giriş noktası olur. İkincisi, aynı dosyada class gibi tip tanımları da yazabilirsin ama top-level satırlardan sonra gelmeleri gerekir. Bunları tuzaklar bölümünde gerçek hata mesajlarıyla göreceğiz.

Proje Dosyası: Bookstore.csproj

Template'in oluşturduğu proje dosyası bu kadar kısa:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net10.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

</Project>

Bu bir MSBuild dosyası; .NET'in build sistemi projeyi bu ayarlara göre derler. Sdk="Microsoft.NET.Sdk" satırı, bir .NET projesinin ihtiyaç duyduğu yüzlerce varsayılan ayarı tek seferde içeri alır. Bu yüzden klasördeki .cs dosyalarını tek tek listelemen gerekmez, hepsi otomatik olarak derlemeye girer.

OutputType Exe, yani çalıştırılabilir bir uygulama. Bir library yazıyor olsaydık bu satır olmazdı. TargetFramework projenin hangi .NET sürümünü hedeflediğini söyler. net10.0 derken hem hangi BCL API'lerini kullanabileceğini hem de compiler'ın varsayılan dil sürümünü belirlemiş olursun.

ImplicitUsings açıkken SDK, en sık kullanılan namespace'ler için senin yerine global using satırları üretir. Console'u using System; yazmadan kullanabilmenin sebebi bu. Üretilen dosya obj/Debug/net10.0/Bookstore.GlobalUsings.g.cs:

// <auto-generated/>
global using System;
global using System.Collections.Generic;
global using System.IO;
global using System.Linq;
global using System.Net.Http;
global using System.Threading;
global using System.Threading.Tasks;

Nullable açıkken compiler, null olabilecek bir değeri kontrol etmeden kullandığında seni uyarır. Bu konunun kendi yazısı var; şimdilik açık kalsın ve uyarıları ciddiye al.

Dikkat edersen dosyada dil sürümünü söyleyen bir satır yok. Compiler bunu TargetFramework'ten çıkarır. Bunu görmek için Program.cs'in içine tek satır yazabilirsin: #error version. Bu direktif derlemeyi bilerek durdurur ve compiler sürümünü yazdırır:

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

net10.0 hedefleyen projede dil sürümü 14.0. Bu serideki kodlar C# 10 ile sınırlı; projenin de seni bu sınırda tutmasını istersen PropertyGroup'a bir satır ekle:

<LangVersion>10</LangVersion>

Aynı #error version artık Language version: 10.0. der. Daha eski bir dil sürümü seçmek desteklenen bir ayar; tersini, yani TargetFramework'ün varsayılanından yeni bir sürüm seçmeyi Microsoft desteklemiyor. Denemen bitince #error satırını silmeyi unutma.

dotnet build ve Çıktı Klasörü

dotnet run her seferinde derleyip çalıştırır. Sadece derlemek istediğinde dotnet build kullanırsın:

$ dotnet build
Restore complete (0.1s)
  Bookstore net10.0 succeeded (0.1s) → bin/Debug/net10.0/Bookstore.dll

Build succeeded in 0.3s

Çıktı bin/Debug/net10.0 klasörüne gider. Debug, varsayılan build configuration'ı; yayına çıkarken -c Release ile optimize edilmiş bir derleme alırsın. Klasörün içi:

$ ls bin/Debug/net10.0
Bookstore
Bookstore.deps.json
Bookstore.dll
Bookstore.pdb
Bookstore.runtimeconfig.json

Asıl ürün Bookstore.dll: IL kodunu içeren assembly. Bookstore.pdb hata ayıklama bilgilerini tutar; exception mesajlarında satır numarası görebilmen onun sayesinde. runtimeconfig.json uygulamanın hangi runtime ile çalışacağını, deps.json bağımlılıklarını anlatır. Uzantısız Bookstore dosyası ise macOS ve Linux'taki çalıştırıcı; Windows'ta bunun yerine Bookstore.exe görürsün. Uygulamayı iki şekilde de başlatabilirsin:

$ dotnet bin/Debug/net10.0/Bookstore.dll
Hello, World!
$ ./bin/Debug/net10.0/Bookstore
Hello, World!

IDE Seçimi

Bir IDE şart değil; bu yazıdaki her şeyi bir metin editörü ve terminalle yaptık. Ama kod tamamlama, anlık hata gösterimi ve debugger için bir IDE hayatını çok kolaylaştırır. Windows'ta Visual Studio 2026 en yaygın seçim, bireysel kullanım için Community sürümü ücretsiz. Her platformda çalışan seçenekler ise C# Dev Kit eklentisiyle Visual Studio Code ve JetBrains Rider. Visual Studio for Mac Ağustos 2024'te emekliye ayrıldı; Mac'teysen VS Code ya da Rider'a bak. Hangisini seçersen seç arka planda aynı SDK'yı ve aynı dotnet komutlarını kullanır. Bu seride IDE'ye bağlı adım anlatmıyoruz, her şey terminalden de yapılabiliyor.

İlk Hata: Derleyici Mesajını Okumak

Hata almadan programlama öğrenilmez, o yüzden bir tane alalım. Program.cs'teki satırın sonundaki noktalı virgülü sil ve derle:

$ dotnet build
Restore complete (0.1s)
  Bookstore net10.0 failed with 1 error(s) (0.1s)
    .../Bookstore/Program.cs(1,35): error CS1002: ; expected

Build failed with 1 error(s) in 0.3s

Satırın başında dosyanın tam yolu yazar, burada kısalttım. Mesajı soldan sağa oku: hangi dosya (Program.cs), hangi satır ve sütun ((1,35)), ne tür bir sorun (error), hangi kod (CS1002) ve açıklama (; expected). Sütun 35, kapanan parantezin hemen sonrası; compiler noktalı virgülü tam orada bekliyordu. C#'ta her statement noktalı virgülle biter.

Şimdi noktalı virgülü geri koy ama WriteLine'ı Writeline diye yaz:

    .../Bookstore/Program.cs(1,9): error CS0117: 'Console' does not contain a definition for 'Writeline'

C# büyük-küçük harfe duyarlı: Writeline ile WriteLine farklı isimler. Hata kodları arama yapmak için de çok değerli; CS0117 diye arattığında Microsoft'un dokümanında bu hatanın açıklamasını ve tipik sebeplerini bulursun.

Üçüncü hata, dil sürümüyle ilgili. Projeye LangVersion 10 eklediysen ve C# 11 ile gelen bir özelliği, örneğin raw string literal'ı kullanırsan compiler seni durdurur:

string json = """{ "title": "Clean Code" }""";
Console.WriteLine(json);
    .../Bookstore/Program.cs(1,15): error CS8936: Feature 'raw string literals' is not available in C# 10.0. Please use language version 11.0 or greater.

Mesaj hem sorunu hem çözümü söylüyor. Eski bir projede çalışırken internette bulduğun yeni bir syntax'ın neden derlenmediğini bu hata sayesinde hemen anlarsın.

Sık Düşülen Tuzaklar

İki dosyada top-level kod yazmak. Projeye ikinci bir .cs dosyası ekleyip içine de bir Console.WriteLine yazarsan derleme durur: error CS8802: Only one compilation unit can have top-level statements. Giriş noktası tek olmalı; diğer dosyalara class'lar ve method'lar koyarsın.

Class'ı top-level kodun üstüne yazmak. Aynı dosyada önce bir class tanımlayıp altına Console.WriteLine yazdığında compiler şunu söyler: error CS8803: Top-level statements must precede namespace and type declarations. Önce çalışan kod, sonra tip tanımları.

Yanlış klasörde dotnet run çalıştırmak. Proje dosyası olmayan bir klasörde Couldn't find a project to run. mesajını alırsın. Ya .csproj'un olduğu klasöre geç ya da --project ile yolu ver.

LangVersion'ı latest yapmak. Eski bir projede yeni bir özelliği kullanmak için bu kestirme cazip gelir; ama Microsoft bu kombinasyonu desteklemiyor. Runtime ya da BCL desteği gerektiren özelliklerde derleme hatasıyla ya da beklenmedik davranışla karşılaşırsın. Yeni dil özelliği istiyorsan TargetFramework'ü yükselt.

Küçük Bir Deneme

★☆☆ dotnet new console -o Bookstore ile projeyi oluştur, çalıştır ve obj klasöründe GlobalUsings.g.cs dosyasını bul.

★★☆ args örneğini programın bütün argümanlarını sayısıyla birlikte yazdıracak şekilde değiştir. İpucu: args.Length ve string.Join(", ", args).

★★★ .csproj'a <ImplicitUsings>disable</ImplicitUsings> yaz ve derle. Hangi hatayı alıyorsun? Programı, dosyanın başına tek bir satır ekleyerek yeniden derlenir hâle getir.

Elinde artık çalışan bir proje ve derleyici mesajlarını okuyabilen bir göz var. Bir sonraki yazıda değişkenlere ve veri tiplerine geçiyoruz: C#'ın hazır tipleri, var'ın aslında ne yaptığı, const, literal'ler, varsayılan değerler ve isimlendirme kuralları.