Eu tenho um grande objeto:
class BigObject{
public int Id {get;set;}
public string FieldA {get;set;}
// ...
public string FieldZ {get;set;}
}
e um objeto especializado, semelhante ao DTO:
class SmallObject{
public int Id {get;set;}
public EnumType Type {get;set;}
public string FieldC {get;set;}
public string FieldN {get;set;}
}
Pessoalmente, acho um conceito de conversão explícita do BigObject no SmallObject - sabendo que é uma operação unidirecional e com perda de dados - muito intuitiva e legível:
var small = (SmallObject) bigOne;
passSmallObjectToSomeone(small);
É implementado usando o operador explícito:
public static explicit operator SmallObject(BigObject big){
return new SmallObject{
Id = big.Id,
FieldC = big.FieldC,
FieldN = big.FieldN,
EnumType = MyEnum.BigObjectSpecific
};
}
Agora, eu poderia criar uma SmallObjectFactory
classe com o FromBigObject(BigObject big)
método, que faria a mesma coisa, adicioná-la à injeção de dependência e chamá-la quando necessário ... mas para mim parece ainda mais complicado e desnecessário.
PS Não tenho certeza se isso é relevante, mas haverá OtherBigObject
que também poderá ser convertido em SmallObject
configurações diferentes EnumType
.
c#
object-oriented
.net
type-casting
Gerino
fonte
fonte
.ToSmallObject()
método (ouGetSmallObject()
). Um lapso momentâneo da razão - eu sabia que algo está errado com o meu pensamento, então eu pedi a vocês :)ToSmallObject
método de @ Telastyn .Respostas:
Nenhuma das outras respostas está certa na minha humilde opinião. Nesta pergunta de stackoverflow, a resposta mais votada argumenta que o código de mapeamento deve ser mantido fora do domínio. Para responder sua pergunta, não - o uso do operador de elenco não é ótimo. Aconselho a criação de um serviço de mapeamento entre o DTO e o objeto do domínio, ou você pode usar o automapper para isso.
fonte
Isso não é ótimo. Eu trabalhei com código que fez esse truque inteligente e que levou à confusão. Afinal, você seria de esperar para ser capaz de simplesmente atribuir o
BigObject
em umaSmallObject
variável, se os objetos são suficientes relacionado para lançá-los. Porém, não funciona - você obtém erros do compilador se tentar, pois, no que diz respeito ao sistema de tipos, eles não são relacionados. Também é um pouco desagradável para o operador de fundição criar novos objetos.Eu recomendaria um
.ToSmallObject()
método. É mais claro sobre o que realmente está acontecendo e sobre como detalhado.fonte
mildly distasteful
é um eufemismo. É lamentável que o idioma permita que esse tipo de coisa pareça uma impressão tipográfica. Ninguém imaginaria que era uma transformação real de objeto, a menos que eles mesmos escrevessem. Em uma equipe de uma pessoa, tudo bem. Se você colabora com alguém, na melhor das hipóteses, é uma perda de tempo, porque você precisa parar e descobrir se é realmente um elenco ou se é uma daquelas transformações loucas..ToSmallObject()
. Dificilmente você deve substituir os operadores.Get
implica retornar uma coisa existente. A menos que você substitua as operações no objeto pequeno, duasGet
chamadas retornarão objetos desiguais, causando confusão / bugs / wtfs.Embora eu possa entender por que você precisaria de um
SmallObject
, eu abordaria o problema de maneira diferente. Minha abordagem para esse tipo de problema é usar uma fachada . Seu único objetivo é encapsularBigObject
e disponibilizar apenas membros específicos. Dessa maneira, é uma nova interface na mesma instância, e não uma cópia. Claro que você também pode querer fazer uma cópia, mas eu recomendaria que você o fizesse através de um método criado para esse fim em combinação com a Fachada (por exemploreturn new SmallObject(instance.Clone())
).O Facade tem uma série de outras vantagens, a saber: garantir que determinadas seções do seu programa só possam fazer uso dos membros disponibilizados através da sua fachada, garantindo efetivamente que ele não possa fazer uso do que não deveria saber. Além disso, ele também tem a enorme vantagem de ter mais flexibilidade nas alterações de
BigObject
manutenção futura sem precisar se preocupar muito com a forma como ela é usada em todo o programa. Contanto que você possa emular o antigo comportamento de uma forma ou de outra, poderá fazer oSmallObject
trabalho da mesma maneira que antes, sem precisar alterar seu programa em todosBigObject
os lugares que seriam usados.Note, isso significa
BigObject
que não depende,SmallObject
mas o contrário (como deveria ser na minha humilde opinião).fonte
SmallObject
está comSmallObject
ouBigObject
. Por padrão, essa abordagem obrigaSmallObject
a evitar dependências de membros privados / protegidos deBigObject
. Podemos dar um passo adiante e evitar dependências de membros privados / protegidosSmallObject
usando umToSmallObject
método de extensão.BigObject
dessa maneira. Se você quisesse fazer algo semelhante, recomendaria criar umToAnotherObject
método de extensão dentroBigObject
? Essas não devem ser as preocupações deBigObject
uma vez que, presumivelmente, ela já é grande o suficiente. Também permite que você se separeBigObject
da criação de suas dependências, o que significa que você pode usar fábricas e similares. A outra abordagem une fortementeBigObject
eSmallObject
. Isso pode ser bom nesse caso específico, mas não é uma prática recomendada na minha humilde opinião.BigObject
sendo acopladoSmallObject
, é apenas um método estático em algum lugar que recebe um argumentoBigObject
e retornaSmallObject
. Os métodos de extensão são realmente apenas açúcar sintático para chamar métodos estáticos de uma maneira mais agradável. O método de extensão não é parte deBigObject
, é um método estático completamente separado. Na verdade, é um bom uso de métodos de extensão e muito útil para conversões de DTO, em particular.Há uma convenção muito forte que se baseia em tipos de referência mutáveis que preservam a identidade. Como o sistema geralmente não permite operadores de conversão definidos pelo usuário em situações em que um objeto do tipo de origem pode ser atribuído a uma referência do tipo de destino, existem apenas alguns casos em que as operações de conversão definidas pelo usuário seriam razoáveis para referência mutável tipos.
Eu sugeriria como requisito que, dado
x=(SomeType)foo;
seguido algum tempo depoisy=(SomeType)foo;
, com ambos os elencos aplicados ao mesmo objeto,x.Equals(y)
fosse sempre e para sempre verdadeiro, mesmo que o objeto em questão fosse modificado entre os dois elencos. Essa situação poderia ser aplicada se, por exemplo, um tivesse um par de objetos de tipos diferentes, cada um com uma referência imutável ao outro, e converter um objeto para o outro tipo retornaria sua instância emparelhada. Também poderia se aplicar a tipos que servem como invólucros para objetos mutáveis, desde que as identidades dos objetos que estão sendo quebrados sejam imutáveis, e dois invólucros do mesmo tipo se reportariam iguais se envoltassem a mesma coleção.Seu exemplo específico usa classes mutáveis, mas não preserva nenhuma forma de identidade; como tal, eu sugeriria que não é um uso apropriado de um operador de fundição.
fonte
Pode estar tudo bem.
Um problema com o seu exemplo é que você usa esses nomes de exemplo. Considerar:
Agora, quando você tem uma boa idéia do significado de long e int , tanto o elenco implícito de
int
paralong
quanto o elenco explícito delong
paraint
são bastante compreensíveis. Também é compreensível como3
se torna3
e é apenas outra maneira de trabalhar3
. É compreensível como isso falharáint.MaxValue + 1
em um contexto verificado. Mesmo como ele funcionaráint.MaxValue + 1
em um contexto desmarcado para resultarint.MinValue
não é a coisa mais difícil de entender.Da mesma forma, quando você lança implicitamente para um tipo de base ou explicitamente para um tipo derivado, é compreensível para quem sabe como a herança funciona o que está acontecendo e qual será o resultado (ou como poderá falhar).
Agora, com BigObject e SmallObject , não tenho noção de como esse relacionamento funciona. Se o seu tipo real é tal que a relação de elenco é óbvia, o elenco pode realmente ser uma boa idéia, embora muitas vezes, talvez a grande maioria, se esse for o caso, isso deve refletir-se na hierarquia de classes e na classe. fundição normal baseada em herança será suficiente.
fonte
BigObject
pode descrever umEmployee {Name, Vacation Days, Bank details, Access to different building floors etc.}
eSmallObject
pode ser umMoneyTransferRecepient {Name, Bank details}
. Há uma tradução direta deEmployee
paraMoneyTransferRecepient
e não há motivo para enviar ao aplicativo bancário mais dados do que o necessário.