J'ai un gros objet:
class BigObject{
public int Id {get;set;}
public string FieldA {get;set;}
// ...
public string FieldZ {get;set;}
}
et un objet spécialisé de type DTO:
class SmallObject{
public int Id {get;set;}
public EnumType Type {get;set;}
public string FieldC {get;set;}
public string FieldN {get;set;}
}
Personnellement, je trouve très intuitif et lisible un concept de conversion explicite de BigObject en SmallObject - sachant qu'il s'agit d'une opération unidirectionnelle avec perte de données.
var small = (SmallObject) bigOne;
passSmallObjectToSomeone(small);
Il est implémenté à l'aide d'un opérateur explicite:
public static explicit operator SmallObject(BigObject big){
return new SmallObject{
Id = big.Id,
FieldC = big.FieldC,
FieldN = big.FieldN,
EnumType = MyEnum.BigObjectSpecific
};
}
Maintenant, je pourrais créer une SmallObjectFactory
classe avec FromBigObject(BigObject big)
méthode, qui ferait la même chose, l'ajouter à l'injection de dépendances et l'appeler en cas de besoin ... mais pour moi, cela semble encore plus compliqué et inutile.
PS Je ne sais pas si cela est pertinent, mais il y en aura OtherBigObject
qui pourront également être convertis en SmallObject
paramètres différents EnumType
.
c#
object-oriented
.net
type-casting
Gerino
la source
la source
.ToSmallObject()
méthode (ouGetSmallObject()
). Un laps de temps momentané - je savais que quelque chose n'allait pas dans ma façon de penser, alors je vous ai demandé les gars :)ToSmallObject
méthode.Réponses:
Aucune des autres réponses ne me convient à mon humble avis. Dans cette question de stackoverflow, la réponse ayant obtenu le vote le plus élevé fait valoir que le code de mappage doit être conservé hors du domaine. Pour répondre à votre question, non - votre utilisation de l'opérateur de distribution n'est pas excellente. Je vous conseille de créer un service de cartographie qui se situe entre votre DTO et votre objet de domaine, ou vous pouvez utiliser automapper pour cela.
la source
C'est ... Pas génial. J'ai travaillé avec du code qui a fait cette astuce astucieuse et cela a semé la confusion. Après tout, vous vous attendez à pouvoir simplement affecter le
BigObject
dans uneSmallObject
variable si les objets sont suffisamment liés pour les convertir . Cela ne fonctionne pas cependant - vous obtenez des erreurs de compilation si vous essayez, car en ce qui concerne le système de type, elles ne sont pas liées. Il est également légèrement désagréable pour l'opérateur de moulage de fabriquer de nouveaux objets.Je recommanderais
.ToSmallObject()
plutôt une méthode. Il est plus clair de ce qui se passe réellement et à peu près aussi verbeux.la source
mildly distasteful
est un euphémisme. Il est regrettable que la langue permette à ce genre de chose de ressembler à un transtypage. Personne ne devinerait que c'était une transformation d'objet réelle à moins qu'ils ne l'écrivent eux-mêmes. Dans une équipe d'une personne, très bien. Si vous collaborez avec quelqu'un, dans le meilleur des cas, c'est une perte de temps car vous devez vous arrêter et déterminer si c'est vraiment un casting, ou si c'est l'une de ces transformations folles..ToSmallObject()
. Vous ne devez presque jamais remplacer les opérateurs.Get
implique le retour d'une chose existante. Sauf si vous avez outrepassé les opérations sur le petit objet, deuxGet
appels retourneraient des objets inégaux, provoquant confusion / bugs / wtfs.Bien que je puisse voir pourquoi vous devriez en avoir un
SmallObject
, j'aborderais le problème différemment. Mon approche de ce type de problème consiste à utiliser une façade . Son seul but est d'encapsulerBigObject
et de ne mettre à disposition que des membres spécifiques. De cette façon, il s'agit d'une nouvelle interface sur la même instance, et non d'une copie. Bien sûr, vous pouvez également effectuer une copie, mais je vous recommande de le faire via une méthode créée à cet effet en combinaison avec la façade (par exemplereturn new SmallObject(instance.Clone())
).La façade présente un certain nombre d'autres avantages, à savoir garantir que certaines sections de votre programme ne peuvent utiliser que les membres mis à disposition via votre façade, garantissant ainsi qu'elle ne peut pas utiliser ce qu'elle ne devrait pas savoir. En plus de cela, il a également l'énorme avantage que vous avez plus de flexibilité pour changer
BigObject
dans la future maintenance sans avoir à vous soucier trop de la façon dont il est utilisé tout au long de votre programme. Tant que vous pouvez émuler l'ancien comportement sous une forme ou une autre, vous pouvez faire en sorte que leSmallObject
travail soit le même qu'avant sans avoir à changer votre programme partout oùBigObject
il aurait été utilisé.Notez que ce moyen
BigObject
ne dépend pasSmallObject
mais plutôt l'inverse (comme il devrait l'être à mon humble avis).la source
SmallObject
incombe àSmallObject
ouBigObject
. Par défaut, cette approche obligeSmallObject
à éviter les dépendances sur les membres privés / protégés deBigObject
. Nous pouvons aller plus loin et éviter les dépendances sur les membres privés / protégésSmallObject
en utilisant uneToSmallObject
méthode d'extension.BigObject
cette façon. Si vous vouliez faire quelque chose de similaire, vous préférez créer uneToAnotherObject
méthode d'extension à l'intérieurBigObject
? Telles ne devraient pas être les préoccupations deBigObject
puisque, vraisemblablement, il est déjà assez grand tel quel. Il vous permet également de vous séparerBigObject
de la création de ses dépendances, ce qui signifie que vous pouvez utiliser des usines et autres. L'autre approche couple fortementBigObject
etSmallObject
. Cela peut être bien dans ce cas particulier, mais ce n'est pas la meilleure pratique à mon humble avis.BigObject
couplé àSmallObject
, c'est juste une méthode statique quelque part qui prend un argument deBigObject
et retourneSmallObject
. Les méthodes d'extension ne sont vraiment que du sucre syntaxique pour appeler les méthodes statiques d'une manière plus agréable. La méthode d'extension n'est pas une partie deBigObject
, il est une méthode statique complètement séparée. C'est en fait une assez bonne utilisation des méthodes d'extension, et très pratique pour les conversions DTO en particulier.Il existe une très forte convention selon laquelle les transtypages sur les types de référence mutables préservent l'identité. Étant donné que le système n'autorise généralement pas les opérateurs de conversion définis par l'utilisateur dans les situations où un objet du type source pourrait être affecté à une référence du type de destination, il n'y a que quelques cas où les opérations de conversion définies par l'utilisateur seraient raisonnables pour une référence mutable les types.
Je suggérerais comme exigence que, étant donné
x=(SomeType)foo;
suivi quelque temps plus tardy=(SomeType)foo;
, les deux transpositions étant appliquées au même objet,x.Equals(y)
devrait toujours et à jamais être vrai, même si l'objet en question a été modifié entre les deux transtypages. Une telle situation pourrait s'appliquer si, par exemple, l'un avait une paire d'objets de types différents, chacun détenant une référence immuable à l'autre, et le fait de convertir l'un ou l'autre objet en l'autre type retournerait son instance appariée. Il pourrait également s'appliquer aux types qui servent de wrappers aux objets mutables, à condition que les identités des objets en train d'être encapsulés soient immuables et que deux wrappers du même type se rapportent comme égaux s'ils encapsulent la même collection.Votre exemple particulier utilise des classes mutables, mais ne préserve aucune forme d'identité; en tant que tel, je dirais que ce n'est pas une utilisation appropriée d'un opérateur de casting.
la source
Ça pourrait aller.
Un problème avec votre exemple est que vous utilisez de tels noms d'exemple. Considérer:
Maintenant, lorsque vous avez une bonne idée de ce que signifie un long et un int , la distribution implicite de
int
tolong
et la distribution explicite delong
toint
sont tout à fait compréhensibles. Il est également compréhensible comment3
devient3
et est juste une autre façon de travailler avec3
. Il est compréhensible que cela échoueint.MaxValue + 1
dans un contexte vérifié. Même la façon dont cela fonctionneraint.MaxValue + 1
dans un contexte non contrôlé pour aboutirint.MinValue
n'est pas la chose la plus difficile à juger.De même, lorsque vous effectuez un cast implicite vers un type de base ou explicitement vers un type dérivé, il est compréhensible pour quiconque sait comment l'héritage fonctionne, ce qui se passe et quel sera le résultat (ou comment il pourrait échouer).
Maintenant, avec BigObject et SmallObject, je ne sais pas comment fonctionne cette relation. Si vos types réels sont tels que la relation de casting est évidente, le casting peut en effet être une bonne idée, bien que la plupart du temps, peut-être la grande majorité, si tel est le cas, cela devrait se refléter dans la hiérarchie des classes et un lancer normal basé sur l'héritage suffira.
la source
BigObject
pourrait décrire unEmployee {Name, Vacation Days, Bank details, Access to different building floors etc.}
, etSmallObject
pourrait être unMoneyTransferRecepient {Name, Bank details}
. Il y a une traduction simple deEmployee
àMoneyTransferRecepient
, et il n'y a aucune raison d'envoyer à l'application bancaire plus de données que nécessaire.