Laden...

Records vs. Class für Models/DTOs

Erstellt von Duesmannr vor 2 Jahren Letzter Beitrag vor 2 Jahren 525 Views
D
Duesmannr Themenstarter:in
161 Beiträge seit 2017
vor 2 Jahren
Records vs. Class für Models/DTOs

Nabend,

ich schreibe mir gerade eine Library mit .NET 6 um entspannter mit einer API zu kommunizieren.
Dahingehend, erstelle ich für jede Response, explizite Klassen/Records, damit ich die direkt typisieren kann.

Bin noch nicht ganz auf den grünen Zweig mit Records gekommen.
Bei meiner Recherche, bin ich auch auf die Top Antwort von dem Stackoverflow Eintrag gestoßen.

Also habe ich meine Models/DTOs als Records definiert.

Funktioniert auch soweit so gut. Nur ist mir dann ein Problem aufgefallen.

Ich habe als Beispiel diesen Record:


public record CreateSessionRequest([property: JsonPropertyName("request_token")] string RequestToken);

Den Record nutze ich bspw. für ein Request.

Für ein zweiten Request brauche ich zu der genannten Property aber auch noch Username und Password. Also Vererbung. Um auch weniger schreiben zu müssen, aber auch die Übersichtlichkeit zu behalten.

Also ein 2. Record, der den oberen Record erbt.


//Ohne Vererbung
public record CreateSessionWithCredentialsRequest(
        [property: JsonPropertyName("username")] string Username,
        [property: JsonPropertyName("password")] string Password
        );

//Mit Vererbung
public record CreateSessionWithCredentialsRequest(
        [property: JsonPropertyName("username")] string Username,
        [property: JsonPropertyName("password")] string Password
        ) : CreateSessionRequest(); //Hier in den Klammern muss die Property RequestToken angegeben werden

Wie im Kommentar erwähnt, kommt hier meine Frage?/Problem auf.
Ich müsste dem 2. Record ebenfalls eine Property 'RequestToken' hinzufügen, damit ich den Wert zum Basis Record übergeben kann.

Das ist jetzt eine Property. Aber ich habe auch Fälle, da sind es gleich mal 20 Properties und da lohnt sich die Vererbung halt nicht, wenn ich jede Property neu definieren muss.

Ich könnte den Record jetzt genau so definieren, wie eine Klasse, weil ich dann einen parameterlosen Konstruktor habe, aber damit entfällt zB. auch die Funktionalität des Deconstructs (sinnig oder nicht, sei dahingestellt).

Und da stelle ich mir die Frage, macht ein Record überhaupt Sinn?
Da kann ich auch gleich normale Klassen verwenden. Und dahingehend, fällt mir dann auch kein Beispiel ein, wann ich denn Records nutzen soll.
Sie sind immutable und reichen Werte weiter. Aber sobald man mit Vererbung arbeiten möchte, würde nach meinem jetzigen Kenntnisstand, Records wegfallen, weil man eben die Properties alle neu definieren muss.

Ich freue mich auf auf Ideen, womit ich das besser verstehen kann oder auch, wenn ich etwas übersehe.

Schönen Abend noch!

2.079 Beiträge seit 2012
vor 2 Jahren

Die Frage, die ich mir stelle:

Brauchst Du Vererbung, oder willst Du dir nur Schreibarbeit ersparen?
Überleg dir gut, ob Du Vererbung nutzen willst, denn nachträglich wieder raus nehmen, kann sehr anstrengend werden.
Oder Du hast schnell einiges an zusätzlicher Komplexität, wenn die Daten größtenteils gleich sind, sich dann aber doch irgendein Detail unterscheidet.

Ersparen von Schreibarbeit ist mMn. nur dann ein Grund, wenn der gesamte Quellcode nicht mit der Basisklasse, sondern einem Interface arbeitet - also eine Art Default-Basis-Implementierung.
Bei den Records sieht die Vererbung eher danach aus, dass das Ziel "echte" Vererbung ist, also um auch die Konzepte zu nutzen und nicht nur um Schreibarbeit zu sparen.

Mir ist aber auch keine Möglichkeit bekannt, außer alles nochmal anzugeben.
Vermutlich geht das auch gewollt, denn bei manchen Funktionen ist ja auch die Reihenfolge der Werte wichtig.

Du könntest natürlich auch einfach normale Klassen nutzen - brauchst Du denn überhaupt das, was bei den Records generiert wird?
Was genau das ist, kannst Du dir mit einem Decompiler oder https://sharplab.io/ anschauen.

NuGet Packages im Code auslesen
lock Alternative für async/await

Beim CleanCode zählen nicht die Regeln, sondern dass wir uns mit diesen Regeln befassen, selbst wenn wir sie nicht befolgen - hoffentlich nach reiflichen Überlegungen.

446 Beiträge seit 2004
vor 2 Jahren

Du meinst sowas oder?


public record CreateSessionWithCredentialsRequest : CreateSessionRequest
{
    public CreateSessionWithCredentialsRequest(string username, string password, string RequestToken) 
        : base(RequestToken)
    {
        Username = username;
        Password = password;
    }

    public string Username { get; set; }
    public string Password { get; set; }
}

und in kurz


public record CreateSessionRequest(string RequestToken);

public record CreateSessionWithCredentialsRequest(string RequestToken, string Username, string Password) : CreateSessionRequest(RequestToken);

PS: Ich habe C# LangVersion 9.0 aktiviert.

Beim record hast du im Unterschied zur normalen Klasse folgendes:

An override of Object.Equals(Object).
A virtual Equals method whose parameter is the record type.
An override of Object.GetHashCode().
Methods for operator == and operator !=.
Record types implement System.IEquatable<T>.

Quelle: https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/records#characteristics-of-records

Schaut mal im IRC vorbei:
Server: https://libera.chat/ ##chsarp

16.835 Beiträge seit 2008
vor 2 Jahren

Ich müsste dem 2. Record ebenfalls eine Property 'RequestToken' hinzufügen, damit ich den Wert zum Basis Record übergeben kann.

Auch Klassen-Konstruktoren brauchen eine Doppeldeklaration in so einem Fall.
Daher ja: musste doppelt deklarieren.

Aber ich habe auch Fälle, da sind es gleich mal 20 Properties und da lohnt sich die Vererbung halt nicht, wenn ich jede Property neu definieren muss.

Das ist in jeder anderen Schreibweise derzeit ebenfalls explizit notwendig.

Und da stelle ich mir die Frage, macht ein Record überhaupt Sinn?

Ja, in den Fällen, in den die Schreiberleichtung sinn macht - und das ist nicht überall der Fall.

Aber sobald man mit Vererbung arbeiten möchte, würde nach meinem jetzigen Kenntnisstand, Records wegfallen, weil man eben die Properties alle neu definieren muss.

Ja und nein; kommt drauf an.