Mert Özen Her Satırda Daha İleri
Geliştirici Araçları IDE'ler — #04

Refactoring Araçları ve Code Style: Quick Actions, Alt+Enter, .editorconfig ve Code Cleanup

Mert Özen 8 Kas 2026 10 dk 4 görüntülenme
Refactoring Araçları ve Code Style: Quick Actions, Alt+Enter, .editorconfig ve Code Cleanup

Code style'ı build'de zorlamak için .editorconfig'te kural ID'siyle severity ve EnforceCodeStyleInBuild gerekir; gerçek ölçümde :warning son ekli IDE0028'i build raporladı, dotnet format görmedi. Üç IDE'de güvenli refactoring kısayolları, Ctrl+. ve Alt+Enter, dotnet format ve Code Cleanup, Rider inspection'ları, Angular template'lerinde rename ve rename'in JSON, reflection ve string'lerde kaçırdıkları.

Sürüm notu: Bu yazıdaki kodlar .NET 10 SDK (Software Development Kit) 10.0.402, Angular 22.2 ve Node.js 24 ile derlenip denenmiştir. Visual Studio 2026 (18.11) ve JetBrains Rider 2026.2 bu makinede (macOS) çalıştırılmadı; bu iki IDE'nin (Integrated Development Environment) menü adları ve kısayolları Microsoft Learn ve JetBrains dokümanından, Visual Studio Code (VS Code) tarafı resmi C# dokümanından aktarıldı.

Önce Tahmin Et

Bookstore'un stok modülünde eski usul yazılmış bir class var: block-scoped namespace, field'a atama yapan klasik constructor, new List<string>() ve kullanılmayan bir using.

using System.Collections.Generic;

namespace Bookstore.Inventory
{
    public class StockService
    {
        private readonly IStockRepository _repository;

        public StockService(IStockRepository repository)
        {
            _repository = repository;
        }

        public List<string> FindLowStock(IEnumerable<string> isbns, int threshold)
        {
            List<string> result = new List<string>();
            // ...
        }
    }
}

Ekip bu stilleri .editorconfig'e :warning olarak yazıyor ve build'de zorluyor. Build dört uyarı veriyor. Sonra biri dotnet format style çalıştırıp "hepsini düzeltti" diyor. Sence düzeltti mi? Cevap, aşağıdaki gerçek çıktılarda; ipucu: bir kural build'de görünüp dotnet format'ta hiç görünmüyor.

Güvenli Refactoring'ler: Üç IDE'de Ne Var

"Güvenli" refactoring, davranışı değiştirmeden yapıyı değiştiren ve bunu derleyicinin symbol bilgisiyle yapan işlemdir: rename bütün referansları günceller, extract method seçili bloğun okuduğu ve yazdığı değişkenleri parametreye ve dönüş değerine çevirir, change signature bütün çağıranları düzenler. Neyin neden refactor edileceği Yazılım Tasarımı serilerindeki Refactoring yazılarının konusu; burada yalnız aletleri konuşuyoruz.

RefactoringVisual Studio 2026Rider (Windows / macOS)VS Code (C# Dev Kit)
Rename (dosya adı dahil)Ctrl+R, Ctrl+RShift+F6 / ⇧F6F2
Extract methodCtrl+R, Ctrl+MCtrl+Alt+M / ⌥⌘MC# refactoring listesinde yok
Introduce variable / parameterCtrl+. menüsüCtrl+Alt+V, Ctrl+Alt+P / ⌥⌘V, ⌥⌘PIntroduce parameter (Ctrl+.)
InlineCtrl+. menüsüCtrl+Alt+N / ⌥⌘NInline method, inline temporary variable
Change signatureCtrl+. menüsü, Ctrl+R, Ctrl+O (sıralama)Ctrl+F6 / ⌘F6C# refactoring listesinde yok
Move type to file / namespaceCtrl+. menüsüF6 (Move)Move type to matching file
Bütün refactoring'lerCtrl+.Ctrl+Alt+Shift+T / ⌃T (Refactor This)Ctrl+Shift+R / ⌃⇧R (Refactor)

Tablo Microsoft Learn'deki Visual Studio kısayol listesi, Rider'ın varsayılan (Windows) ve IntelliJ macOS keymap'leri ile VS Code'un "C# Quick Actions and Refactorings" sayfasından derlendi. VS Code tarafındaki boşluklar önemli bir gerçeği gösteriyor: VS Code'un C# refactoring'leri Roslyn'in code action'larından ibaret; Visual Studio'nun diyalog tabanlı Change Signature'ı ya da Rider'ın Move/Safe Delete gibi solution çapındaki refactoring'leri orada aynı derinlikte yok.

Ctrl+. ve Alt+Enter: Analyzer, Code Fix ve Refactoring

Ampul ikonu (Visual Studio ve VS Code'da Ctrl+., macOS VS Code'da ⌘.; Rider'da Alt+Enter, macOS'ta ⌥⏎) üç farklı şeyi aynı menüde gösterir. Code fix, bir analyzer'ın bulduğu sorunu düzeltir: IDExxxx kuralları code style'dır (ör. IDE0161 file-scoped namespace), CAxxxx kuralları kalite ve güvenilirlik analizidir (ör. CA2016 CancellationToken'ı aktarmamak). Refactoring ise bir sorun olmadan da sunulan dönüşümdür: if'i switch'e çevirmek gibi. Rider'da aynı menü ReSharper inspection'larının quick-fix'leri ve context action'larıyla dolar; 2026.2 ile gelen intention preview, bir quick-fix'in üreteceği diff'i uygulamadan önce gösteriyor.

Visual Studio dokümanı Quick Actions için Ctrl+.'yı veriyor; Rider'dan gelip Alt+Enter alışkanlığını korumak istiyorsan Tools > Options > Environment > Keyboard'da bu tuşun hangi komuta bağlı olduğunu kontrol et. Analyzer kurallarının hangisinin neden açılacağı, CI (Continuous Integration) tarafında statik analiz politikası ise Test ve Kalite kategorisindeki ilerideki Statik Analiz serisinin konusu.

.editorconfig ile Severity ve Build'de Zorlamak

Code style kuralları IDE'de öneri olarak kalırsa ekipte herkesin ampulüne kalır. Kuralı repo'ya yazıp build'de zorlamak için iki şey gerekiyor: .editorconfig'te severity ve projede EnforceCodeStyleInBuild.

root = true

[*.cs]
indent_style = space
indent_size = 4

# file-scoped namespace (IDE0161)
csharp_style_namespace_declarations = file_scoped:warning

# primary constructor (IDE0290)
csharp_style_prefer_primary_constructors = true:warning

# collection expression (IDE0028, IDE0300-IDE0305)
dotnet_style_prefer_collection_expression = when_types_loosely_match:warning

# gereksiz using (IDE0005)
dotnet_diagnostic.IDE0005.severity = warning
<PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>

İlk build üç stil uyarısı ve bir sürpriz verdi (satır sonlarındaki doküman linkini ve proje yolunu kısalttık):

./Bookstore.Inventory/StockService.cs(14,16): warning IDE0290: Use primary constructor
./Bookstore.Inventory/StockService.cs(21,35): warning IDE0028: Collection initialization can be simplified
./Bookstore.Inventory/StockService.cs(3,11): warning IDE0161: Convert to file-scoped namespace
CSC : warning EnableGenerateDocumentationFile: Set MSBuild property 'GenerateDocumentationFile' to 'true' in project file to enable IDE0005 (Remove unnecessary usings/imports) on build

IDE0005 (gereksiz using) IDE'de görünür ama build'de ancak XML (Extensible Markup Language) dokümantasyon dosyası üretilirken çalışır; derleyici bunu kendisi söylüyor. Projeye <GenerateDocumentationFile>true</GenerateDocumentationFile> ve public üyelerde yorum zorunluluğunu susturmak için <NoWarn>$(NoWarn);CS1591</NoWarn> ekledik; dördüncü uyarı geldi: StockService.cs(1,1): warning IDE0005: Using directive is unnecessary.

dotnet format ve Code Cleanup

IDE'deki "bütün dosyada düzelt" komutunun CLI (Command Line Interface) karşılığı dotnet format. İlk denemede bir parametre hatası yaptık:

$ dotnet format style Bookstore.Inventory --severity warning --verify-no-changes
Argument 'warning' not recognized. Must be one of:
	'info'
	'warn'
	'error'
	'hidden'

.editorconfig'te warning yazılıyor, dotnet format'ta warn. Doğrusuyla:

$ dotnet format style Bookstore.Inventory --severity warn --verify-no-changes
./Bookstore.Inventory/StockService.cs(3,11): warning IDE0161: Convert to file-scoped namespace
./Bookstore.Inventory/StockService.cs(1,1): warning IDE0005: Using directive is unnecessary.
./Bookstore.Inventory/StockService.cs(14,16): warning IDE0290: Use primary constructor
$ echo $?
2

Önce Tahmin Et'in cevabı burada: build dört uyarı verdi, dotnet format üç tane gördü. IDE0028 listede yok. dotnet format style --severity warn ile düzeltip yeniden build aldığımızda üç uyarı gitti, IDE0028 kaldı. Sebep, severity'nin seçenek satırının sonuna (:warning) yazılması; build bu sözdizimini bu kural için uyguladı, dotnet format uygulamadı. Kuralın ID'siyle açık severity verince fark kapandı:

dotnet_diagnostic.IDE0028.severity = warning
$ dotnet format style Bookstore.Inventory --severity warn --verify-no-changes
./Bookstore.Inventory/StockService.cs(14,31): warning IDE0028: Collection initialization can be simplified
$ dotnet format style Bookstore.Inventory --severity warn
$ dotnet build Bookstore.Inventory
Build succeeded.

Ders: build'de zorlayacağın her kural için dotnet_diagnostic.<ID>.severity satırını ayrıca yaz; CI'da hem build'i hem dotnet format --verify-no-changes'i çalıştır. Sonuç kodu:

namespace Bookstore.Inventory;

public class StockService(IStockRepository repository)
{
    private readonly IStockRepository _repository = repository;

    public List<string> FindLowStock(IEnumerable<string> isbns, int threshold)
    {
        List<string> result = [];
        // ...
    }
}

Primary constructor dönüşümü _repository field'ını korudu ve parametreden başlattı; bu güvenli taraf, çünkü field readonly kalıyor. Field'ı tamamen kaldırıp parametreyi doğrudan kullanmak (parametre mutable olduğu için) ayrı bir karar.

IDE tarafında aynı işi kayıtta yaptırabilirsin. Visual Studio'da Code Cleanup iki profil tutar (Editor'ün altındaki süpürge ikonu); Tools > Options > Text Editor > Code Cleanup altında Run Code Cleanup profile on save açılırsa her kayıtta seçili düzeltmeler uygulanır. Rider'da Settings > Tools > Actions on Save'de reformat ve cleanup seçilir; Code Cleanup'ın elle kısayolu varsayılan keymap'te Ctrl+R, C. VS Code'da editor.formatOnSave ve editor.codeActionsOnSave ayarları var. Kayıtta otomatik düzeltmenin bedeli, dokunmadığın satırların da diff'e girmesi; eski bir kod tabanında önce bütün repo'yu tek seferde ayrı bir commit'le formatla, sonra kayıtta düzeltmeyi aç.

Rider: Inspection'lar ve Solution-Wide Analysis

Rider'ın analizinin çekirdeği ReSharper inspection'ları: yüzlerce kural, her biri bir severity ile (Error, Warning, Suggestion, Hint) ve çoğu bir quick-fix ile. Rider Roslyn analyzer'larını da çalıştırır ve .editorconfig'i okur; .NET'in standart code style seçeneklerinin yanında ReSharper'a özgü seçenekleri de .editorconfig'e yazabilirsin. Kendi ayarları için katmanlı bir sistem var: kişisel ayarlar, solution klasöründeki team-shared .DotSettings dosyası ve kişisel solution katmanı. Ekip kararı olan bir inspection severity'si team-shared katmanda, kişisel tercih kişisel katmanda durmalı.

Solution-Wide Error Analysis (SWEA), açık olmayan dosyalardaki hataları da sürekli analiz eder; bir interface'e member eklediğinde implement etmeyen bütün class'lar Problems penceresinde (Alt+6) anında listelenir. Büyük solution'larda bellek ve CPU (Central Processing Unit) maliyeti var; Rider bu yüzden açıp kapatmayı status bar'dan kolaylaştırıyor.

Angular Tarafı: ESLint, Prettier ve Template'leri Kapsayan Rename

TypeScript tarafında code style'ın karşılığı ESLint (Angular için angular-eslint kuralları) ve biçim için Prettier; ikisi de package.json'daki script'lerle CI'da çalışır ve üç IDE'de extension ya da yerleşik destekle editörde görünür. Bookstore web projesinde Prettier 3.8 zaten devDependency.

Angular'a özgü kritik nokta template'ler. Bir component'in property'si HTML (HyperText Markup Language) template'te kullanılıyorsa, rename'in template'i de güncellemesi gerekir; bunu Angular Language Service yapar (VS Code'da Angular ekibinin extension'ı, Rider'da yerleşik WebStorm desteği, Visual Studio'da Microsoft'un dağıttığı extension). Language Service'siz bir editörde yalnız TypeScript dosyasında title'ı name'e çevirdiğimizde şu oldu:

$ npx ng build --configuration development
Application bundle generation failed. [0.389 seconds]

✘ [ERROR] TS2339: Property 'title' does not exist on type 'Book'. [plugin angular-compiler]

    src/app/app.html:1:14:
      1 │ <h1>{{ book().title }}</h1>
        ╵               ~~~~~

  Error occurs in the template of component App.

Angular derleyicisinin template type-checking'i template'teki referansı tip olarak doğruladığı için hata build'de yakalandı. Bu güvenlik ağıdır, rename aracı değil: dinamik erişimler (book()[key]), string ile kurulan form kontrol adları ve backend'den gelen JSON (JavaScript Object Notation) alan adları derleyicinin göremeyeceği yerlerdir.

Rename'in Yakalayamadığı Yerler

C# tarafında da aynı sınır var. Bir record'u JSON'a çevirip, bir yerde reflection ile, bir yerde nameof ile property okuyan küçük bir program:

Book book = new("978-0135957059", "The Pragmatic Programmer", 49.90m);

// 1) JSON sözleşmesi property adını izler
Console.WriteLine(JsonSerializer.Serialize(book));

// 2) string literal ile reflection: rename bunu göremez
PropertyInfo? byString = typeof(Book).GetProperty("Title");
Console.WriteLine($"GetProperty(\"Title\"): {byString?.Name ?? "null"}");

// 3) nameof bir symbol referansıdır: rename onu günceller
PropertyInfo? byNameof = typeof(Book).GetProperty(nameof(Book.Title));
Console.WriteLine($"GetProperty(nameof(Book.Title)): {byNameof?.Name ?? "null"}");

public sealed record Book(string Isbn, string Title, decimal Price);
{"Isbn":"978-0135957059","Title":"The Pragmatic Programmer","Price":49.90}
GetProperty("Title"): Title
GetProperty(nameof(Book.Title)): Title

Şimdi IDE'nin yapacağı gibi Title'ı Name'e rename ediyoruz: yalnız symbol referansları değişiyor, yani record parametresi ve nameof(Book.Title). String'ler olduğu gibi kalıyor:

{"Isbn":"978-0135957059","Name":"The Pragmatic Programmer","Price":49.90}
GetProperty("Title"): null
GetProperty(nameof(Book.Title)): Name

Kod derlendi, testlerin derleyiciye dayananları geçti, ama iki şey bozuldu: API'nin (Application Programming Interface) JSON sözleşmesi Title'dan Name'e döndü ve Angular tarafındaki book.title artık undefined; string ile yapılan reflection null döndürdü. nameof kullanan satır ise doğru çalıştı. Sözleşmeyi koda bağlamak için property'nin JSON adını açıkça yazmak gerekiyor:

public sealed record Book(string Isbn, [property: JsonPropertyName("Title")] string Name, decimal Price);
{"Isbn":"978-0135957059","Title":"The Pragmatic Programmer","Price":49.90}

Visual Studio'nun rename penceresinde "Include strings" ve "Include comments", Rider'ın rename diyaloğunda yorum ve string'lerdeki geçişleri arama seçenekleri var; ikisi de metin eşleşmesidir ve sana gösterip onay ister. Razor dosyaları Visual Studio ve Rider'da rename'e dahil edilir, ama @Html.ActionLink("Details", "Book") gibi string ile verilen action ve controller adları dahil değildir.

Üretimde Dikkat

Public sözleşmeyi rename'den koru. API DTO'ları (Data Transfer Object), mesaj kontratları ve veritabanına giden Entity Framework Core (EF Core) entity'leri için JSON adını ya da kolon adını attribute veya configuration ile sabitle; böylece C# tarafındaki rename sözleşmeyi değiştirmez. EF Core'da property rename'i bir migration'da kolonun silinip yeniden eklenmesi olarak görünebilir; migration'ı mutlaka oku (EF Core serisinde migration yazıları).

Code style'ı kademeli zorla. Mevcut bir kod tabanında bütün kuralları bir anda warning yapıp TreatWarningsAsErrors açarsan ilk build binlerce hata verir. Önce suggestion, sonra tek seferlik dotnet format commit'i, sonra warning.

Büyük solution'da Rider'ın SWEA'sı ve Visual Studio'nun tam solution analizi bellek ister. Yavaşlık varsa önce analiz kapsamını (yalnız açık dosyalar ya da tüm solution) kontrol et.

Sık Düşülen Tuzaklar

Rename'i metin değiştirme sanmak ya da tersine metin değiştirmeyi rename sanmak. Semantik rename string, reflection, JSON adı ve dinamik template erişimini görmez; Find and Replace ise alakasız symbol'leri de değiştirir.

Severity'yi yalnız :warning son ekiyle vermek. Gerçek ölçüm: build IDE0028'i raporladı, dotnet format görmedi. dotnet_diagnostic.<ID>.severity satırı bu farkı kapattı.

--severity warning yazmak. dotnet format warn bekler ve yanlış değerde hata verip çıkar; CI script'inde bu hata yeşil bir adımın arkasına saklanmasın diye exit code'u kontrol et.

IDE0005'in build'de neden çıkmadığını anlamamak. GenerateDocumentationFile kapalıyken build bu kuralı çalıştırmaz; derleyici bunu bir uyarıyla söylüyor.

Kayıtta otomatik cleanup'ı eski bir dosyada açmak. Küçük bir düzeltmenin pull request'i yüzlerce satırlık format değişikliğiyle gelir ve review edilemez hale gelir.

Küçük Bir Deneme

★☆☆ Kendi projende EnforceCodeStyleInBuild'i aç ve yalnız IDE0161'i warning yap. Kaç dosya uyarı veriyor? dotnet format style --diagnostics IDE0161 --severity warn ile yalnız bu kuralı düzelt.

★★☆ Bir API DTO'sunda bir property'yi IDE ile rename et ve integration testlerin ya da Angular tarafının bunu yakalayıp yakalamadığına bak. Yakalamıyorsa sözleşmeyi koruyan bir test yaz: serialize edilmiş JSON'daki alan adlarını sabit bir listeyle karşılaştır.

★★★ CI pipeline'ına iki adım ekle: dotnet build -warnaserror yalnız stil kuralları için (ipucu: WarningsAsErrors ile ID listesi) ve dotnet format --verify-no-changes. İkisinin raporladığı kurallar aynı mı? Farklıysa sebebini bul.

Kodu değiştirmeyi öğrendik; sırada değiştirdiğimiz kodu çalıştırıp debug etmek var. Bir sonraki yazıda launchSettings.json, VS Code'un launch.json'ı ve compound configuration'larla .NET API ile Angular'ı tek tuşla birlikte debug ediyoruz.