0%

C# Geliştirici İpuçları

C# ile daha verimli kod yazmanızı sağlayacak ipuçları ve yöntemler.

📅
👤CodeBros
⏱️6 dakikada okunur
C#.NETTips
C# Geliştirici İpuçları - Blog yazısı öne çıkan görseli

C# Geliştirici İpuçları

C# ile çalışan geliştiriciler için performans, okunabilirlik ve maintainability açısından önemli ipuçları ve best practice'ler. Bu yazıda deneyimli geliştiricilerin kullandığı teknikleri paylaşacağım.

1. Modern C# Özellikleri

Null-conditional Operators

// Eski yöntem
if (user != null && user.Profile != null && user.Profile.Address != null)
{
    var city = user.Profile.Address.City;
}

// Modern yöntem
var city = user?.Profile?.Address?.City;

Pattern Matching

// C# 8.0+ Switch Expressions
public string GetDiscountType(decimal amount) => amount switch
{
    < 100 => "No discount",
    >= 100 and < 500 => "Bronze",
    >= 500 and < 1000 => "Silver",
    >= 1000 => "Gold",
    _ => throw new ArgumentException("Invalid amount")
};

// C# 11+ List Patterns
public string AnalyzeNumbers(int[] numbers) => numbers switch
{
    [] => "Empty array",
    [var x] => $"Single element: {x}",
    [var x, var y] => $"Two elements: {x}, {y}",
    [var first, .., var last] => $"Multiple elements: {first}...{last}",
    _ => "Unknown pattern"
};

Record Types

// Immutable record
public record Person(string Name, int Age);

// Mutable record
public record PersonMutable
{
    public string Name { get; set; }
    public int Age { get; set; }
}

// Usage
var person = new Person("John", 30);
var olderPerson = person with { Age = 31 }; // Creates new instance

2. Performans İpuçları

StringBuilder vs String Concatenation

// Yavaş - her concatenation'da yeni string oluşur
string result = "";
for (int i = 0; i < 1000; i++)
{
    result += $"Item {i}";
}

// Hızlı - StringBuilder kullanımı
var sb = new StringBuilder();
for (int i = 0; i < 1000; i++)
{
    sb.Append($"Item {i}");
}
string result = sb.ToString();

Span ve Memory

// Heap allocation olmadan string slice
ReadOnlySpan<char> span = "Hello, World!".AsSpan();
ReadOnlySpan<char> hello = span.Slice(0, 5); // "Hello"

// Array işlemleri için Span kullanımı
int[] numbers = { 1, 2, 3, 4, 5 };
Span<int> span = numbers.AsSpan();
var slice = span.Slice(1, 3); // [2, 3, 4]

Async/Await Best Practices

// ConfigureAwait(false) kullanımı
public async Task<string> GetDataAsync()
{
    var result = await httpClient.GetStringAsync(url)
        .ConfigureAwait(false);
    return result;
}

// ValueTask kullanımı - sıkça çağrılan metodlar için
public ValueTask<int> GetCachedValueAsync(string key)
{
    if (_cache.TryGetValue(key, out var value))
        return new ValueTask<int>(value); // Synchronous completion
    
    return GetValueFromDatabaseAsync(key); // Asynchronous completion
}

3. LINQ İpuçları

Performanslı LINQ Kullanımı

// Yavaş - multiple enumeration
var numbers = GetNumbers();
var evenCount = numbers.Where(x => x % 2 == 0).Count();
var evenSum = numbers.Where(x => x % 2 == 0).Sum();

// Hızlı - single enumeration
var evenNumbers = GetNumbers().Where(x => x % 2 == 0).ToList();
var evenCount = evenNumbers.Count;
var evenSum = evenNumbers.Sum();

Useful LINQ Extensions

public static class LinqExtensions
{
    public static IEnumerable<T> WhereNotNull<T>(this IEnumerable<T?> source)
        where T : class
    {
        return source.Where(x => x != null)!;
    }
    
    public static IEnumerable<(T item, int index)> WithIndex<T>(this IEnumerable<T> source)
    {
        return source.Select((item, index) => (item, index));
    }
    
    public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
    {
        foreach (var item in source)
            action(item);
    }
}

// Kullanım
var names = new[] { "John", null, "Jane", null };
var validNames = names.WhereNotNull().WithIndex();

4. Exception Handling

Custom Exceptions

public class BusinessException : Exception
{
    public string ErrorCode { get; }
    public object? Details { get; }
    
    public BusinessException(string message, string errorCode, object? details = null) 
        : base(message)
    {
        ErrorCode = errorCode;
        Details = details;
    }
}

// Usage
if (user.Balance < amount)
{
    throw new BusinessException(
        "Insufficient balance", 
        "INSUFFICIENT_BALANCE",
        new { CurrentBalance = user.Balance, RequestedAmount = amount }
    );
}

Global Exception Handler

public class GlobalExceptionMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<GlobalExceptionMiddleware> _logger;

    public GlobalExceptionMiddleware(RequestDelegate next, ILogger<GlobalExceptionMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "An unhandled exception occurred");
            await HandleExceptionAsync(context, ex);
        }
    }

    private async Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        context.Response.ContentType = "application/json";
        
        var response = exception switch
        {
            BusinessException businessEx => new { 
                StatusCode = 400, 
                Message = businessEx.Message, 
                ErrorCode = businessEx.ErrorCode 
            },
            NotFoundException => new { 
                StatusCode = 404, 
                Message = "Resource not found" 
            },
            _ => new { 
                StatusCode = 500, 
                Message = "An internal server error occurred" 
            }
        };

        context.Response.StatusCode = response.StatusCode;
        await context.Response.WriteAsync(JsonSerializer.Serialize(response));
    }
}

5. Dependency Injection Patterns

Service Lifetimes

// Startup.cs veya Program.cs
services.AddSingleton<IConfigurationService, ConfigurationService>();
services.AddScoped<IUserService, UserService>();
services.AddTransient<IEmailService, EmailService>();

// Factory Pattern
services.AddScoped<IServiceFactory, ServiceFactory>();
services.AddScoped<Func<ServiceType, IBaseService>>(provider => serviceType =>
{
    return serviceType switch
    {
        ServiceType.Email => provider.GetService<IEmailService>(),
        ServiceType.Sms => provider.GetService<ISmsService>(),
        _ => throw new ArgumentException($"Unknown service type: {serviceType}")
    };
});

Options Pattern

public class EmailSettings
{
    public string SmtpServer { get; set; } = string.Empty;
    public int Port { get; set; }
    public string Username { get; set; } = string.Empty;
    public string Password { get; set; } = string.Empty;
}

// Registration
services.Configure<EmailSettings>(configuration.GetSection("EmailSettings"));

// Usage
public class EmailService
{
    private readonly EmailSettings _settings;
    
    public EmailService(IOptions<EmailSettings> options)
    {
        _settings = options.Value;
    }
}

6. Testing İpuçları

Unit Test Best Practices

[TestClass]
public class UserServiceTests
{
    private Mock<IUserRepository> _userRepositoryMock;
    private UserService _userService;
    
    [TestInitialize]
    public void Setup()
    {
        _userRepositoryMock = new Mock<IUserRepository>();
        _userService = new UserService(_userRepositoryMock.Object);
    }
    
    [TestMethod]
    public async Task GetUserAsync_ValidId_ReturnsUser()
    {
        // Arrange
        var userId = 1;
        var expectedUser = new User { Id = userId, Name = "John" };
        _userRepositoryMock
            .Setup(x => x.GetByIdAsync(userId))
            .ReturnsAsync(expectedUser);
        
        // Act
        var result = await _userService.GetUserAsync(userId);
        
        // Assert
        Assert.IsNotNull(result);
        Assert.AreEqual(expectedUser.Name, result.Name);
        _userRepositoryMock.Verify(x => x.GetByIdAsync(userId), Times.Once);
    }
}

Integration Tests

[TestClass]
public class UserControllerIntegrationTests
{
    private WebApplicationFactory<Program> _factory;
    private HttpClient _client;
    
    [TestInitialize]
    public void Setup()
    {
        _factory = new WebApplicationFactory<Program>();
        _client = _factory.CreateClient();
    }
    
    [TestMethod]
    public async Task GetUser_ValidId_ReturnsUser()
    {
        // Arrange
        var userId = 1;
        
        // Act
        var response = await _client.GetAsync($"/api/users/{userId}");
        
        // Assert
        response.EnsureSuccessStatusCode();
        var content = await response.Content.ReadAsStringAsync();
        var user = JsonSerializer.Deserialize<User>(content);
        Assert.IsNotNull(user);
    }
}

7. Memory Management

IDisposable Pattern

public class ResourceManager : IDisposable
{
    private bool _disposed = false;
    private readonly FileStream _fileStream;
    private readonly Timer _timer;
    
    public ResourceManager(string filePath)
    {
        _fileStream = new FileStream(filePath, FileMode.Open);
        _timer = new Timer(TimerCallback, null, 0, 1000);
    }
    
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    
    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                _fileStream?.Dispose();
                _timer?.Dispose();
            }
            _disposed = true;
        }
    }
    
    ~ResourceManager()
    {
        Dispose(false);
    }
}

Using Statements

// Traditional using
using (var stream = new FileStream("file.txt", FileMode.Open))
{
    // Process file
}

// Using declaration (C# 8.0+)
using var stream = new FileStream("file.txt", FileMode.Open);
// Stream automatically disposed at end of scope

8. Configuration ve Environment

Structured Configuration

public class ApplicationSettings
{
    public DatabaseSettings Database { get; set; } = new();
    public EmailSettings Email { get; set; } = new();
    public ApiSettings Api { get; set; } = new();
}

public class DatabaseSettings
{
    public string ConnectionString { get; set; } = string.Empty;
    public int CommandTimeout { get; set; } = 30;
}

// appsettings.json
{
  "ApplicationSettings": {
    "Database": {
      "ConnectionString": "Server=localhost;Database=MyDb;",
      "CommandTimeout": 60
    },
    "Email": {
      "SmtpServer": "smtp.gmail.com",
      "Port": 587
    }
  }
}

Sonuç

Bu ipuçları, C# ile daha verimli ve maintainable kod yazmanızı sağlayacak. Özellikle:

  • Modern C# özelliklerini kullanarak kodu daha okunabilir hale getirin
  • Performans optimizasyonlarını ihmal etmeyin
  • LINQ'u doğru şekilde kullanın
  • Exception handling stratejinizi belirleyin
  • Dependency injection pattern'larını öğrenin
  • Testing kültürünü benimseyin
  • Memory management konusunda dikkatli olun

Bu teknikleri projelerinizde uyguladığınızda, kodunuzun kalitesinde büyük bir artış göreceksiniz.

K
CodeBros
CodeBros - Profesyonel Yazılım Geliştirme Şirketi
Paylaş:
WhatsApp
WhatsApp