it-swarm.com.de

Ist meine Verwendung eines expliziten Casting-Operators sinnvoll oder ein schlechter Hack?

Ich habe ein großes Objekt:

class BigObject{
    public int Id {get;set;}
    public string FieldA {get;set;}
    // ...
    public string FieldZ {get;set;}
}

und ein spezialisiertes, DTO-ähnliches Objekt:

class SmallObject{
    public int Id {get;set;}
    public EnumType Type {get;set;}
    public string FieldC {get;set;}
    public string FieldN {get;set;}
}

Ich persönlich finde ein Konzept, BigObject explizit in SmallObject umzuwandeln - in dem Wissen, dass es sich um eine Einwegoperation handelt, bei der Daten verloren gehen - sehr intuitiv und lesbar:

var small = (SmallObject) bigOne;
passSmallObjectToSomeone(small);

Es wird mit einem expliziten Operator implementiert:

public static explicit operator SmallObject(BigObject big){
    return new SmallObject{
        Id = big.Id,
        FieldC = big.FieldC,
        FieldN = big.FieldN,
        EnumType = MyEnum.BigObjectSpecific
    };
}

Jetzt könnte ich mit der Methode FromBigObject(BigObject big) eine SmallObjectFactory -Klasse erstellen, die das Gleiche tut, sie zur Abhängigkeitsinjektion hinzufügt und bei Bedarf aufruft ... aber mir scheint es noch mehr zu sein überkompliziert und unnötig.

PS Ich bin mir nicht sicher, ob dies relevant ist, aber es wird OtherBigObject geben, das auch in SmallObject konvertiert werden kann, wobei unterschiedliche EnumType festgelegt werden.

25
Gerino

Keine der anderen Antworten hat meiner bescheidenen Meinung nach Recht. In dieser Stackoverflow-Frage argumentiert die am höchsten bewertete Antwort, dass Mapping-Code aus der Domäne herausgehalten werden sollte. Um Ihre Frage zu beantworten, nein - Ihre Verwendung des Cast-Operators ist nicht großartig. Ich würde empfehlen, einen Zuordnungsdienst zu erstellen, der sich zwischen Ihrem DTO und Ihrem Domänenobjekt befindet, oder Sie könnten dafür Automapper verwenden.

0

Es ist ... nicht großartig. Ich habe mit Code gearbeitet, der diesen cleveren Trick gemacht hat und zu Verwirrung geführt hat. Schließlich würden Sie erwarten, dass Sie die Variable BigObject einfach einer Variablen SmallObject zuweisen können, wenn die Objekte so verwandt sind, dass sie umgewandelt werden können. Es funktioniert jedoch nicht - Sie erhalten Compilerfehler, wenn Sie es versuchen, da sie in Bezug auf das Typsystem nicht miteinander zusammenhängen. Es ist für den Casting-Bediener auch leicht unangenehm, neue Objekte herzustellen.

Ich würde stattdessen eine .ToSmallObject() Methode empfehlen. Es ist klarer darüber, was tatsächlich vor sich geht und wie ausführlich.

81
Telastyn

Während ich sehen kann, warum Sie ein SmallObject benötigen würden, würde ich das Problem anders angehen. Mein Ansatz für diese Art von Problem ist die Verwendung eines Fassade . Sein einziger Zweck ist es, BigObject zu kapseln und nur bestimmte Mitglieder verfügbar zu machen. Auf diese Weise handelt es sich um eine neue Schnittstelle in derselben Instanz und nicht um eine Kopie. Natürlich können Sie auch eine Kopie erstellen, aber ich würde empfehlen, dass Sie dies durch eine zu diesem Zweck erstellte Methode in Kombination mit der Fassade tun ( zum Beispiel return new SmallObject(instance.Clone())).

Fassade hat eine Reihe weiterer Vorteile, nämlich sicherzustellen, dass bestimmte Abschnitte Ihres Programms nur die durch Ihre Fassade zur Verfügung gestellten Mitglieder nutzen können, um effektiv zu gewährleisten, dass sie nicht das nutzen können, was sie nicht wissen sollten. Darüber hinaus hat es den enormen Vorteil, dass Sie bei zukünftigen Wartungsarbeiten mehr Flexibilität beim Ändern von BigObject haben, ohne sich zu viele Gedanken darüber machen zu müssen, wie es in Ihrem Programm verwendet wird. Solange Sie das alte Verhalten in irgendeiner Form emulieren können, können Sie dafür sorgen, dass SmallObject genauso funktioniert wie zuvor, ohne dass Sie Ihr Programm überall ändern müssen, wo BigObject verwendet worden wäre.

Beachten Sie, dass dies bedeutet, dass BigObject nicht von SmallObject abhängt, sondern umgekehrt (wie es meiner bescheidenen Meinung nach sein sollte).

11
Neil

Es gibt eine sehr starke Konvention, dass Casts auf veränderbare Referenztypen identitätserhaltend sind. Da das System im Allgemeinen keine benutzerdefinierten Casting-Operatoren in Situationen zulässt, in denen ein Objekt des Quelltyps einer Referenz des Zieltyps zugewiesen werden könnte, gibt es nur wenige Fälle, in denen benutzerdefinierte Casting-Operationen für eine veränderbare Referenz sinnvoll wären Typen.

Ich würde als Voraussetzung vorschlagen, dass, wenn x=(SomeType)foo; gefolgt von y=(SomeType)foo; gegeben wird und beide Casts auf dasselbe Objekt angewendet werden, x.Equals(y) immer und für immer sein sollte true, auch wenn das betreffende Objekt zwischen den beiden Casts geändert wurde. Eine solche Situation könnte zutreffen, wenn z. eines hatte ein Paar von Objekten unterschiedlichen Typs, von denen jedes einen unveränderlichen Verweis auf das andere enthielt, und das Umwandeln eines Objekts in den anderen Typ würde seine gepaarte Instanz zurückgeben. Dies könnte auch für Typen gelten, die als Wrapper für veränderbare Objekte dienen, vorausgesetzt, die Identitäten der zu verpackenden Objekte sind unveränderlich, und zwei Wrapper desselben Typs würden sich als gleich melden, wenn sie gleich verpackt würden Sammlung.

Ihr spezielles Beispiel verwendet veränderbare Klassen, behält jedoch keine Form der Identität bei. als solches würde ich vorschlagen, dass es keine angemessene Verwendung eines Casting-Operators ist.

6
supercat

Es könnte in Ordnung sein.

Ein Problem mit Ihrem Beispiel ist, dass Sie solche beispielhaften Namen verwenden. Erwägen:

SomeMethod(long longNum)
{
  int num = (int)longNum;
  /* ... */

Wenn Sie nun eine gute Idee haben, was ein long und int bedeutet, dann sowohl die implizite Umwandlung von int in long als auch Die explizite Besetzung von long nach int ist durchaus verständlich. Es ist auch verständlich, wie aus 33 Wird und ist nur eine andere Möglichkeit, mit 3 Zu arbeiten. Es ist verständlich, wie dies mit int.MaxValue + 1 In einem überprüften Kontext fehlschlägt. Selbst wie es mit int.MaxValue + 1 In einem ungeprüften Kontext funktioniert, um zu int.MinValue Zu führen, ist nicht das Schwierigste.

Wenn Sie implizit in einen Basistyp oder explizit in einen abgeleiteten Typ umwandeln, ist dies für jeden verständlich, der weiß, wie Vererbung funktioniert, was passiert und was das Ergebnis sein wird (oder wie es fehlschlagen könnte).

Mit BigObject und SmallObject habe ich keine Ahnung, wie diese Beziehung funktioniert. Wenn Ihre realen Typen so sind, dass die Casting-Beziehung offensichtlich ist, dann ist Casting in der Tat eine gute Idee, obwohl die meiste Zeit, vielleicht die überwiegende Mehrheit, wenn dies der Fall ist, sollte es sich in der Klassenhierarchie und der widerspiegeln normales vererbungsbasiertes Casting wird ausreichen.

1
Jon Hanna