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 tip
  • record 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:

  • Order bir entity’dir → class
  • OrderResponse veri taşır → record
  • CreateOrderCommand veri taşır → record
  • OrderPrice değ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