Mam duży przedmiot:
class BigObject{
public int Id {get;set;}
public string FieldA {get;set;}
// ...
public string FieldZ {get;set;}
}
oraz specjalistyczny obiekt podobny do DTO:
class SmallObject{
public int Id {get;set;}
public EnumType Type {get;set;}
public string FieldC {get;set;}
public string FieldN {get;set;}
}
Osobiście uważam koncepcję jawnego przeniesienia BigObject do SmallObject - wiedząc, że jest to operacja jednokierunkowa, polegająca na utracie danych - bardzo intuicyjna i czytelna:
var small = (SmallObject) bigOne;
passSmallObjectToSomeone(small);
Jest realizowany za pomocą jawnego operatora:
public static explicit operator SmallObject(BigObject big){
return new SmallObject{
Id = big.Id,
FieldC = big.FieldC,
FieldN = big.FieldN,
EnumType = MyEnum.BigObjectSpecific
};
}
Teraz mógłbym stworzyć SmallObjectFactory
klasę z FromBigObject(BigObject big)
metodą, która zrobiłaby to samo, dodać ją do wstrzykiwania zależności i wywołać w razie potrzeby ... ale dla mnie wydaje się to jeszcze bardziej skomplikowane i niepotrzebne.
PS Nie jestem pewien, czy to jest istotne, ale będzie też to, OtherBigObject
co będzie można przekonwertować SmallObject
, ustawiając inaczej EnumType
.
c#
object-oriented
.net
type-casting
Gerino
źródło
źródło
.ToSmallObject()
metoda (lubGetSmallObject()
). Chwilowy brak rozumu - wiedziałem, że coś jest nie tak z moim myśleniem, więc poprosiłem was :)ToSmallObject
.Odpowiedzi:
Żadna z pozostałych odpowiedzi nie ma racji w mojej skromnej opinii. W tym pytaniu dotyczącym przepełnienia stosu odpowiedź o najwyższym głosowaniu dowodzi, że kod mapowania powinien być trzymany poza domeną. Aby odpowiedzieć na twoje pytanie, nie - twoje użycie operatora rzutowania nie jest świetne. Radziłbym stworzyć usługę mapowania, która znajduje się pomiędzy twoim DTO a twoim obiektem domeny, lub możesz użyć do tego automappera.
źródło
To jest ... Nie świetnie. Pracowałem z kodem, który wykonał tę sprytną sztuczkę i doprowadził do zamieszania. W końcu można oczekiwać, że można przypisać zmienną
BigObject
doSmallObject
zmiennej, jeśli obiekty są wystarczająco powiązane, aby je rzutować. Jednak to nie działa - jeśli spróbujesz, pojawią się błędy kompilatora, ponieważ jeśli chodzi o system typów, nie są one powiązane. Wykonywanie nowych obiektów jest również nieco niesmaczne dla operatora odlewania..ToSmallObject()
Zamiast tego poleciłbym metodę. Wyraźniej mówi o tym, co się dzieje, i co jest tak szczegółowe.źródło
mildly distasteful
to mało powiedziane. To niefortunne, że język pozwala tego rodzaju wyglądać jak rzutowanie czcionek. Nikt nie zgadłby, że to rzeczywista transformacja obiektu, chyba że sami to napisali. W zespole jednoosobowym, w porządku. Jeśli współpracujesz z kimkolwiek, w najlepszym przypadku jest to strata czasu, ponieważ musisz przestać i zastanowić się, czy to naprawdę obsada, czy też jest to jedna z tych szalonych transformacji..ToSmallObject()
. Rzadko należy zastępować operatorów.Get
oznacza zwrot istniejącej rzeczy. O ile nie przesłonisz operacji na małym obiekcie, dwaGet
wywołania zwrócą nierówne obiekty, powodując zamieszanie / błędy / wtfs.Chociaż rozumiem, dlaczego trzeba mieć
SmallObject
, podchodzę do problemu inaczej. Moje podejście do tego typu problemów polega na użyciu elewacji . Jego jedynym celem jest enkapsulacjaBigObject
i udostępnianie tylko określonych członków. W ten sposób jest to nowy interfejs w tej samej instancji, a nie kopia. Oczywiście możesz również chcieć wykonać kopię, ale radziłbym to zrobić za pomocą metody stworzonej w tym celu w połączeniu z fasadą (na przykładreturn new SmallObject(instance.Clone())
).Fasada ma wiele innych zalet, a mianowicie zapewnia, że niektóre części twojego programu mogą korzystać tylko z członków udostępnionych przez twoją fasadę, skutecznie gwarantując, że nie będzie w stanie wykorzystać tego, o czym nie powinna wiedzieć. Oprócz tego ma tę ogromną zaletę, że masz większą elastyczność w zmienianiu
BigObject
przyszłej konserwacji, bez martwienia się zbytnio o to, jak jest używany w twoim programie. Tak długo, jak możesz naśladować stare zachowanie w takiej czy innej formie, możesz uczynićSmallObject
pracę taką samą, jak wcześniej, bez konieczności zmiany programu wszędzieBigObject
, gdzie byś był używany.Zauważ, że to
BigObject
nie zależy od,SmallObject
ale na odwrót (tak powinno być moim skromnym zdaniem).źródło
SmallObject
spoczywa naSmallObject
czyBigObject
. Domyślnie takie podejście wymuszaSmallObject
unikanie zależności od prywatnych / chronionych członkówBigObject
. Możemy pójść o krok dalej i uniknąć zależności od prywatnych / chronionych członkówSmallObject
za pomocąToSmallObject
metody rozszerzenia.BigObject
ten sposób ryzykujesz bałagan . Jeśli chciałbyś zrobić coś podobnego, kupiłbyś możliwość utworzeniaToAnotherObject
metody rozszerzeniaBigObject
? Nie powinny to być obawy,BigObject
ponieważ prawdopodobnie jest już wystarczająco duży. Pozwala także oddzielić sięBigObject
od tworzenia jego zależności, co oznacza, że można korzystać z fabryk i tym podobnych. Drugie podejście silnie parujeBigObject
iSmallObject
. W tym konkretnym przypadku może to być w porządku, ale moim skromnym zdaniem nie jest to najlepsza praktyka.BigObject
sprzężonySmallObject
, jest to tylko metoda statyczna, która wymaga argumentuBigObject
i zwracaSmallObject
. Metody rozszerzeń to tak naprawdę tylko cukier syntaktyczny, który pozwala ładniej wywoływać metody statyczne. Metoda rozszerzenie nie jest część zBigObject
, jest to całkowicie odrębny sposób statyczny. W rzeczywistości jest to całkiem dobre wykorzystanie metod rozszerzeń i bardzo przydatne zwłaszcza w przypadku konwersji DTO.Istnieje bardzo silna konwencja, która opiera się na zmiennych typach referencyjnych, które zachowują tożsamość. Ponieważ system generalnie nie zezwala na operatory rzutowania zdefiniowane przez użytkownika w sytuacjach, w których obiekt typu źródłowego może być przypisany do odwołania do typu docelowego, istnieje tylko kilka przypadków, w których operacje rzutowania zdefiniowane przez użytkownika byłyby uzasadnione dla odniesienia zmiennego typy.
Zasugerowałbym jako wymóg, aby, biorąc pod uwagę, że
x=(SomeType)foo;
późniejy=(SomeType)foo;
, z obiema rzutami zastosowanymi do tego samego obiektu,x.Equals(y)
zawsze i zawsze byłaby prawdą, nawet jeśli przedmiotowy przedmiot został zmodyfikowany między dwoma rzutami. Taka sytuacja mogłaby mieć zastosowanie, gdyby np. Jeden miał parę obiektów różnego typu, z których każdy posiadał niezmienne odniesienie do drugiego, a rzutowanie jednego z obiektów na inny typ zwróciłoby jego sparowaną instancję. Może to również dotyczyć typów, które służą jako opakowania dla obiektów zmiennych, pod warunkiem, że tożsamości pakowanych obiektów są niezmienne, a dwa opakowania tego samego typu zgłaszałyby się jako równe, gdyby opakowały tę samą kolekcję.Twój konkretny przykład wykorzystuje zmienne klasy, ale nie zachowuje żadnej formy tożsamości; jako taki sugerowałbym, że nie jest to właściwe użycie operatora odlewania.
źródło
To może być w porządku.
Problem z twoim przykładem polega na tym, że używasz takich przykładowych nazw. Rozważać:
Teraz, gdy masz dobry pomysł, co oznacza „ długa” i „ int” , zarówno ukryta rzutowanie na,
int
jaklong
i jawne rzutowanie zlong
naint
są całkiem zrozumiałe. Zrozumiałe jest również, w jaki sposób się3
staje3
i jest po prostu innym sposobem pracy3
. Jest zrozumiałe, jak to się nie powiedzieint.MaxValue + 1
w sprawdzonym kontekście. Nawet to, jak będzie działaćint.MaxValue + 1
w niesprawdzonym kontekście, nie skutkujeint.MinValue
najtrudniejszą rzeczą.Podobnie, gdy rzutujesz pośrednio na typ bazowy lub jawnie na typ pochodny, jest to zrozumiałe dla każdego, kto wie, jak dziedziczenie działa, co się dzieje i jaki będzie wynik (lub jak może się nie powieść).
Teraz z BigObject i SmallObject nie mam pojęcia, jak ta relacja działa. Jeśli twoje prawdziwe typy są takie, że relacja rzutowania jest oczywista, to rzutowanie może rzeczywiście być dobrym pomysłem, chociaż przez większość czasu, być może zdecydowaną większość, jeśli tak jest, to powinno to znaleźć odzwierciedlenie w hierarchii klas i wystarczy normalne odlewanie oparte na dziedzictwie.
źródło
BigObject
może opisaćEmployee {Name, Vacation Days, Bank details, Access to different building floors etc.}
, aSmallObject
może byćMoneyTransferRecepient {Name, Bank details}
. Istnieje proste tłumaczenie zEmployee
naMoneyTransferRecepient
i nie ma powodu, aby przesyłać do aplikacji bankowej więcej danych niż jest to potrzebne.