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

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.



