Il mio utilizzo dell'operatore di casting esplicito è ragionevole o un brutto attacco?

24

Ho un grosso oggetto:

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

e un oggetto specializzato, simile a DTO:

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

Personalmente trovo un concetto di casting esplicito di BigObject in SmallObject - sapendo che si tratta di un'operazione a perdita di dati a senso unico, molto intuitiva e leggibile:

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

Viene implementato utilizzando l'operatore esplicito:

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

Ora, potrei creare una classe SmallObjectFactory con il metodo FromBigObject(BigObject big) , che farebbe la stessa cosa, aggiungerla all'iniezione delle dipendenze e chiamarla quando necessario ... ma a me sembra ancora più complicata e superflua.

PS Non sono sicuro che sia pertinente, ma ci sarà OtherBigObject che sarà anche in grado di essere convertito in SmallObject , impostando% diversoEnumType.

    
posta Gerino 05.05.2015 - 13:27
fonte

5 risposte

0

Nessuna delle altre risposte ha ragione a mio modesto parere. In questa domanda StackOverflow la risposta più votata sostiene che la mappatura il codice dovrebbe essere tenuto fuori dal dominio. Per rispondere alla tua domanda, no, il tuo utilizzo dell'operatore di cast non è eccezionale. Vorrei consigliare di creare un servizio di mappatura che si trovi tra il tuo DTO e il tuo oggetto dominio, oppure potresti usare automapper per questo.

    
risposta data 06.05.2015 - 11:50
fonte
81

È ... Non eccezionale. Ho lavorato con il codice che ha fatto questo trucco intelligente e ha portato alla confusione. Dopotutto, ti aspetteresti di essere in grado di assegnare appena BigObject a una variabile SmallObject se gli oggetti sono correlati abbastanza per il cast. Tuttavia, non funziona - si verificano errori del compilatore se si prova sin da quando si parla del sistema di tipi, non sono correlati. È anche leggermente spiacevole per l'operatore del casting creare nuovi oggetti.

Suggerirei invece un metodo .ToSmallObject() . È più chiaro su ciò che sta effettivamente succedendo in modo dettagliato.

    
risposta data 05.05.2015 - 13:43
fonte
11

Anche se posso capire perché è necessario avere un SmallObject , vorrei affrontare il problema in modo diverso. Il mio approccio a questo tipo di problema è utilizzare una Facciata . Il suo unico scopo è incapsulare BigObject e rendere disponibili solo membri specifici. In questo modo, è una nuova interfaccia sulla stessa istanza e non una copia. Ovviamente puoi anche voler eseguire una copia, ma ti consiglio di farlo tramite un metodo creato a tale scopo in combinazione con la facciata (ad esempio return new SmallObject(instance.Clone()) ).

La facciata presenta una serie di altri vantaggi, ovvero garantire che alcune sezioni del tuo programma possano utilizzare solo i membri messi a disposizione attraverso la facciata, garantendo in modo efficace che non possa fare uso di ciò che non dovrebbe sapere. Oltre a ciò, ha anche l'enorme vantaggio di avere una maggiore flessibilità nel cambiare BigObject nella manutenzione futura senza doversi preoccupare troppo di come viene utilizzato nel programma. Finché puoi emulare il vecchio comportamento in una forma o nell'altra, puoi fare in modo che SmallObject funzioni come prima senza dover modificare il tuo programma ovunque% sarebbe stato utilizzato% co_de.

Nota, questo significa che BigObject non dipende da BigObject ma piuttosto viceversa (come dovrebbe essere a mio modesto parere).

    
risposta data 05.05.2015 - 15:02
fonte
6

C'è una convenzione molto strong che lancia su tipi di riferimento mutabili che preservano l'identità. Poiché il sistema in genere non consente agli operatori di casting definiti dall'utente in situazioni in cui un oggetto del tipo di origine potrebbe essere assegnato a un riferimento del tipo di destinazione, ci sono solo alcuni casi in cui le operazioni di fusione definite dall'utente sarebbero ragionevoli per riferimento mutabile tipi.

Suggerirei come un requisito che, dato x=(SomeType)foo; seguito qualche tempo dopo da y=(SomeType)foo; , con entrambi i cast applicati allo stesso oggetto, x.Equals(y) dovrebbe sempre e per sempre essere vero, anche se l'oggetto in questione era modificato tra i due calchi. Tale situazione potrebbe applicarsi se ad es. uno aveva una coppia di oggetti di diverso tipo, ognuno dei quali conteneva un riferimento immutabile all'altro, e il cast di un oggetto all'altro restituiva la sua istanza accoppiata. Potrebbe anche essere applicato con tipi che servono da wrapper a oggetti mutabili, a condizione che le identità degli oggetti da incartare fossero immutabili e che due wrapper dello stesso tipo si dichiarassero uguali se avvolgessero lo stesso raccolta.

Il tuo particolare esempio usa classi mutevoli, ma non conserva alcuna forma di identità; come tale, suggerirei che non è un uso appropriato di un operatore di casting.

    
risposta data 05.05.2015 - 20:46
fonte
1

Potrebbe andare bene.

Un problema con il tuo esempio è che usi questi nomi ish di esempio. Prendere in considerazione:

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

Ora, quando hai una buona idea di cosa significhi un lungo e int , allora sia il cast implicito di int a long e il cast esplicito da long a int sono abbastanza comprensibili. È anche comprensibile come 3 diventa 3 ed è solo un altro modo di lavorare con 3 . È comprensibile come questo fallirà con int.MaxValue + 1 in un contesto controllato. Anche come funzionerà con int.MaxValue + 1 in un contesto non controllato per generare int.MinValue non è la cosa più difficile da convincere.

Allo stesso modo, quando lanci implicitamente su un tipo di base o esplicitamente su un tipo derivato è comprensibile a chiunque sappia come funziona l'ereditarietà cosa sta accadendo e quale sarà il risultato (o come potrebbe fallire).

Ora, con BigObject e SmallObject non ho idea di come funzioni questa relazione. Se i tuoi veri tipi in cui tale che la relazione di casting sia ovvia, allora il casting potrebbe essere davvero una buona idea, anche se molto spesso, forse la stragrande maggioranza, se questo è il caso allora dovrebbe riflettersi nella gerarchia delle classi e sarà sufficiente la normale trasmissione basata sull'eredità.

    
risposta data 06.05.2015 - 14:26
fonte

Leggi altre domande sui tag