DEV Community

Properties, getters/setters, και init, αρχικοποίηση πεδίων, διαφορά DTO vs EF Entities

🔍 Κατανόηση των Properties, Getters/Setters και Initialization στην C#

Εισαγωγή

Στη C#, τα properties είναι ο τρόπος με τον οποίο εκθέτουμε δεδομένα (fields) μιας κλάσης με ελεγχόμενο τρόπο. Αντί να κάνουμε απευθείας public τα πεδία, χρησιμοποιούμε properties με getters και setters, ώστε να μπορούμε να προσθέσουμε λογική ελέγχου, προστασία και συμβατότητα με serialization frameworks.


🧱 Τι είναι τα Properties

Ένα property είναι ένας συνδυασμός:

  • ενός getter, που διαβάζει την τιμή ενός πεδίου, και
  • ενός setter, που τη γράφει.

Παράδειγμα:

public class User
{
    public string Name { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

Αυτό είναι ένα auto-property, που σημαίνει πως η C# δημιουργεί εσωτερικά ένα private field για σένα.

Μπορείς όμως να το γράψεις και πιο ρητά:

private string _name;

public string Name
{
    get => _name;
    set => _name = value;
}
Enter fullscreen mode Exit fullscreen mode

🧩 Γιατί να κάνω τον Setter private;

Το private set χρησιμοποιείται όταν θέλουμε το property να είναι μόνο αναγνώσιμο εκτός της κλάσης, αλλά να μπορεί να αλλάξει μόνο εσωτερικά από την ίδια την κλάση.

Παράδειγμα:

public class Order
{
    public Guid Id { get; private set; }
    public DateTime CreatedAt { get; private set; }

    public Order()
    {
        Id = Guid.NewGuid();
        CreatedAt = DateTime.UtcNow;
    }
}
Enter fullscreen mode Exit fullscreen mode

👉 Εδώ, ο Order μπορεί να δημιουργήσει μόνος του το Id και το CreatedAt, αλλά κανείς άλλος δεν μπορεί να τα αλλάξει απ’ έξω.

📌 Χρησιμότητα:

  • Ακεραιότητα αντικειμένου (Encapsulation) — δεν επιτρέπεις σε εξωτερικό κώδικα να "πειράξει" σημαντικά properties.
  • Domain consistency — ιδανικό σε Domain Models ή Read-Only DTOs.
  • Ελεγχόμενη τροποποίηση — μόνο μέσα από methods, όχι με άμεση ανάθεση.

💡 Τι εξυπηρετούν τα Properties γενικά

Encapsulation — προστατεύεις την εσωτερική κατάσταση.
Validation — μπορείς να ελέγχεις τιμές όταν γίνεται set.
Serialization — frameworks όπως JSON serializers δουλεύουν πάνω στα properties.
Data Binding — σε UI frameworks, τα properties είναι θεμελιώδη για binding.


⚙️ Τι είναι το init και πότε το χρησιμοποιούμε

Από την C# 9.0 και μετά, έχουμε τον init accessor.
Αυτό επιτρέπει immutable αντικείμενα, αλλά με ευκολία αρχικοποίησης κατά τη δημιουργία.

Παράδειγμα:

public class Product
{
    public string Name { get; init; }
    public decimal Price { get; init; }
}
Enter fullscreen mode Exit fullscreen mode

Μπορούμε να το χρησιμοποιήσουμε έτσι:

var p = new Product { Name = "Laptop", Price = 999.99m };
Enter fullscreen mode Exit fullscreen mode

Αλλά δεν μπορούμε να αλλάξουμε τις τιμές μετά:

p.Price = 1200; // ❌ Compile error
Enter fullscreen mode Exit fullscreen mode

📌 Χρησιμοποιούμε init όταν θέλουμε:

  • immutable models (π.χ. DTOs, ViewModels),
  • ασφάλεια από μεταβολές μετά την κατασκευή του αντικειμένου,
  • καθαρό, predictable state.

🧮 Τι είναι το string.Empty και γιατί το χρησιμοποιούμε στην αρχικοποίηση

Όταν αρχικοποιούμε ένα property, π.χ. σε ένα DTO, συχνά βλέπουμε:

public string Name { get; set; } = string.Empty;

Enter fullscreen mode Exit fullscreen mode

Αυτό γίνεται για να αποφύγουμε το null στα models που προορίζονται για serialization, binding ή απλή μεταφορά δεδομένων (DTOs).

📌 Γιατί:

  • Όταν κάνεις JSON serialization, αν Name == null, μπορεί να λείψει από το JSON
  • Όταν κάνεις binding σε UI, ένα null string μπορεί να προκαλέσει exceptions ή warnings.
  • Γενικά, τα DTOs είναι "dumb objects", δεν χρειάζονται null state.

Αντίθετα, σε entity models (π.χ. EF Core):

public string? Name { get; set; }
Enter fullscreen mode Exit fullscreen mode

✅ Εκεί επιτρέπουμε το null, γιατί το EF πρέπει να αντικατοπτρίζει την πραγματική βάση δεδομένων, όπου μια στήλη μπορεί να είναι nullable.

📊 Σύνοψη:

Τύπος Μοντέλου Συνήθης Αρχικοποίηση Λόγος
DTO / ViewModel = string.Empty; Αποφυγή null references
EF Entity = null!; ή string? Αντιστοίχιση με DB nulls

🧱 DTOs vs Entities

DTOs (Data Transfer Objects)

  • Ελαφριά μοντέλα που μεταφέρουν δεδομένα μεταξύ layers ή μέσω API.
  • Δεν έχουν επιχειρησιακή λογική.
  • Συνήθως immutable (με init ή private setters).
  • Συχνά αρχικοποιούνται με string.Empty, για αποφυγή nulls.

Entities (EF Core)

  • Αντιστοιχούν άμεσα σε πίνακες βάσης δεδομένων.
  • Μπορεί να έχουν navigation properties, tracking, lazy loading.
  • Πρέπει να επιτρέπουν null όπου η βάση το επιτρέπει.
  • Δεν θέλουμε default τιμές που δεν αντικατοπτρίζουν το DB state.

Extra Tip: Defensive vs Pragmatic Initialization

  • Defensive initialization (DTOs):
public string Name { get; set; } = string.Empty;
Enter fullscreen mode Exit fullscreen mode

→ Προστατεύει από null, χρήσιμο σε API responses.

  • Pragmatic initialization (Entities):
public string? Name { get; set; }
Enter fullscreen mode Exit fullscreen mode

→ Εξασφαλίζει σωστό mapping με τη βάση.


🔒 Παράδειγμα πλήρους κατανόησης

public class CustomerEntity // EF Core Entity
{
    public int Id { get; set; }
    public string? Name { get; set; } // nullable στη DB
    public string? Email { get; set; }
}

public class CustomerDto // DTO
{
    public string Name { get; init; } = string.Empty;
    public string Email { get; init; } = string.Empty;
}
Enter fullscreen mode Exit fullscreen mode

👉 Ο CustomerEntity καθρεφτίζει τη βάση.
👉 Ο CustomerDto είναι ασφαλής για API χρήση, χωρίς nulls, immutable.


🔚 Συμπέρασμα

get; set; → πλήρως αναγνώσιμο/εγγράψιμο property.
get; private set; → εξωτερικά read-only, εσωτερικά εγγράψιμο.
init → immutable μετά τη δημιουργία.
string.Empty στα DTOs → αποφυγή nulls σε serialization/UI.
null στα Entities → συμβατότητα με DB schema.
Ορθό encapsulation → θεμέλιο του καθαρού, συντηρήσιμου κώδικα.


Top comments (0)