C# Class vs Record: Farkları, Kullanım Senaryoları ve Gerçek Proje Örnekleri
Aşağıda C# açısından class ve record konusunu pratik proje mantığıyla anlatıyorum.
1. Class nedir?
class, C#’ta nesne üretmek için kullanılan temel referans tipidir. Bir class’tan oluşturulan nesne bellekte heap üzerinde tutulur; değişken ise nesnenin kendisini değil, o nesneye giden referansı taşır.
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
var user1 = new User { Id = 1, Name = "Emre" };
var user2 = user1;
user2.Name = "Ahmet";
Console.WriteLine(user1.Name); // Ahmet
Burada `user1` ve `user2` aynı nesneyi gösterir.
---
## 2. Record nedir?
`record`, C# 9 ile gelen ve özellikle veri taşıyan modeller için tasarlanmış özel bir türdür.
```csharp
public record UserDto(int Id, string Name);
var user1 = new UserDto(1, "Emre");
var user2 = new UserDto(1, "Emre");
Console.WriteLine(user1 == user2); // True
Record’larda varsayılan olarak değer bazlı eşitlik vardır.
3. Class ve Record arasındaki temel fark
Class için kimlik önemlidir:
Bu aynı nesne mi?
Record için veri önemlidir:
Bu nesnenin içindeki bilgiler aynı mı?
Örnek:
public class ProductClass
{
public int Id { get; set; }
public string Name { get; set; }
}
public record ProductRecord(int Id, string Name);
var c1 = new ProductClass { Id = 1, Name = "Laptop" };
var c2 = new ProductClass { Id = 1, Name = "Laptop" };
var r1 = new ProductRecord(1, "Laptop");
var r2 = new ProductRecord(1, "Laptop");
Console.WriteLine(c1 == c2); // False
Console.WriteLine(r1 == r2); // True
4. Mutable yapı için class kullanımı
Class genellikle değiştirilebilir nesneler için uygundur.
public class Basket
{
public int Id { get; set; }
public List<string> Products { get; set; } = new();
public void AddProduct(string product)
{
Products.Add(product);
}
}
5. Immutable veri için record kullanımı
Record özellikle DTO, request, response ve value object gibi yapılarda idealdir.
public record CreateUserRequest(string Name, string Email);
6. with kullanımı
Record’ların güçlü özelliklerinden biri with ifadesidir.
public record UserDto(int Id, string Name, string Email);
var user1 = new UserDto(1, "Emre", "[email protected]");
var user2 = user1 with
{
Email = "[email protected]"
};
Bu yapı immutable çalışma mantığında oldukça değerlidir.
7. ToString farkı
Class:
Console.WriteLine(user);
// Namespace.UserClass
Record:
Console.WriteLine(user);
// UserRecord { Id = 1, Name = Emre }
8. Deconstruction desteği
Record’lar otomatik deconstruct desteği sunar.
public record UserDto(int Id, string Name);
var user = new UserDto(1, "Emre");
var (id, name) = user;
9. Entity için neden class tercih edilir?
Entity’lerde kimlik önemlidir.
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public void ChangePrice(decimal newPrice)
{
if (newPrice <= 0)
throw new ArgumentException();
Price = newPrice;
}
}
Entity’ler davranış içerir ve zamanla değişebilir.
10. DTO için neden record uygundur?
public record ProductResponse(
int Id,
string Name,
decimal Price
);
DTO yapıları çoğunlukla sadece veri taşır.
11. Value Object için record kullanımı
public record Money(decimal Amount, string Currency);
var money1 = new Money(100, "TRY");
var money2 = new Money(100, "TRY");
Console.WriteLine(money1 == money2); // True
12. Service ve Repository neden class olur?
public class PaymentService
{
public void Pay(decimal amount)
{
// ödeme işlemi
}
}
Service ve repository yapıları davranış taşır.
13. CQRS tarafında record kullanımı
public record CreateProductCommand(
string Name,
decimal Price
);
public record GetProductByIdQuery(int Id);
14. record class ve record struct farkı
public record Person(string Name);
public record struct Point(int X, int Y);
public readonly record struct Coordinate(int X, int Y);
record class→ referans tiprecord struct→ value type
15. Gerçek proje örneği
Entity:
public class Order
{
public int Id { get; private set; }
public int CustomerId { get; private set; }
public decimal TotalAmount { get; private set; }
public string Status { get; private set; }
public void Approve()
{
if (Status != "Pending")
throw new InvalidOperationException();
Status = "Approved";
}
}
DTO:
public record OrderResponse(
int Id,
int CustomerId,
decimal TotalAmount,
string Status
);
Command:
public record CreateOrderCommand(
int CustomerId,
List<int> ProductIds
);
Value Object:
public record OrderPrice(decimal Amount, string Currency);
16. Kısa karar tablosu
| Senaryo | Tercih |
|---|---|
| Entity | class |
| Service | class |
| Repository | class |
| Mutable nesne | class |
| DTO | record |
| API Request | record |
| API Response | record |
| Value Object | record |
| Event modeli | record |
| Command/Query | record |
17. En net kullanım kuralı
Class kullan:
- Nesnenin kimliği önemliyse
- Nesne davranış içeriyorsa
- Nesne zamanla değişiyorsa
Record kullan:
- Nesne sadece veri taşıyorsa
- Immutable yapı istiyorsan
- Değer bazlı equality gerekiyorsa
Pratikte şöyle düşünebilirsin:
Bu nesne iş yapıyor mu? → class
Bu nesne sadece veri mi taşıyor? → record
Bu nesnenin identity'si önemli mi? → class
Bu nesnenin değerleri önemli mi? → record
Modern .NET projelerinde profesyonel yaklaşım genellikle şu şekildedir:
- Entity, service ve davranış içeren yapılarda →
class - DTO, request, response, command, query ve value object yapılarında →
record
18. Kısa karar tablosu
| Senaryo | Tercih |
|---|---|
| Entity | class |
| Service | class |
| Repository | class |
| Controller | class |
| Manager | class |
| Mutable nesne | class |
| DTO | record |
| API Request/Response | record |
| Value Object | record |
| Event modeli | record |
| Command/Query modeli | record |
| Config/options modeli | class veya record |
| EF Core entity | genellikle class |
| Log/event payload | record |
19. Gerçek proje örneği
Entity:
public class Order
{
public int Id { get; private set; }
public int CustomerId { get; private set; }
public decimal TotalAmount { get; private set; }
public string Status { get; private set; }
public void Approve()
{
if (Status != "Pending")
throw new InvalidOperationException("Only pending orders can be approved.");
Status = "Approved";
}
}
DTO:
public record OrderResponse(
int Id,
int CustomerId,
decimal TotalAmount,
string Status
);
Command:
public record CreateOrderCommand(
int CustomerId,
List<int> ProductIds
);
Value Object:
public record OrderPrice(decimal Amount, string Currency);
Burada ayrım nettir:
Orderbir entity’dir →classOrderResponseveri taşır →recordCreateOrderCommandveri taşır →recordOrderPricedeğer nesnesidir →record
20. En net kural
Class kullan:
- Nesnenin kimliği önemliyse
- Nesne davranış içeriyorsa
- Nesne zamanla değişiyorsa
Record kullan:
- Nesne sadece veri taşıyorsa
- Immutable yapı istiyorsan
- Değer bazlı equality gerekiyorsa
Pratikte şöyle düşünebilirsin:
Bu nesne iş yapıyor mu? → class
Bu nesne sadece veri mi taşıyor? → record
Bu nesnenin identity'si önemli mi? → class
Bu nesnenin değerleri önemli mi? → record
Bu nesne zamanla değişiyor mu? → class
Bu nesne oluşturulduktan sonra sabit kalmalı mı? → record
Modern .NET projelerinde profesyonel yaklaşım genellikle şu şekildedir:
- Entity, service ve davranış içeren yapılarda →
class - DTO, request, response, command, query ve value object yapılarında →
record