Tak. Ale odpowiedź jest nieco dziwna. Pierwsza część daje przyzwoitą ilość sensu; druga część jest po prostu całkowicie dziwna. Przejdźmy przez to.
struct Generic<T> {
var args: T
func display() {
print(Printer.print(args))
}
}
Prawidłowa przeciążenia wybrać dla print
decyduje w czasie kompilacji, nie starcie. To najbardziej dezorientuje ludzi. Chcą traktować Swift jak JavaScript, gdzie wszystko jest dynamiczne. Swift lubi być statyczny, ponieważ wtedy może upewnić się, że typy są poprawne i może wykonywać wiele optymalizacji (i Swift kocha, aby zrobić optymalizacje kompilatora). Więc, czas kompilacji, jakiego typu jest args
? Cóż, to jest T
. Czy T
jest znane jako Printable
? Nie, nie jest. Więc używa wersji innej niż Printable
.
Ale kiedy Swift specjalizuje się w Generic
używając PrintableObj
, czy nie wie w tym momencie, że jest to Printable
? Czy w tym momencie kompilator nie mógł utworzyć innej wersji display
? Tak, gdybyśmy wiedzieli podczas kompilacji każdego wywołującego, który kiedykolwiek istniałby w tej funkcji, i że żadne z nich nigdy nie zostanie rozszerzone na Printable
(co może się zdarzyć w zupełnie innym module). Trudno to rozwiązać bez tworzenia wielu dziwacznych przypadków narożnych (gdzie na przykład wewnętrzne rzeczy zachowują się inaczej niż publiczne) i bez zmuszania Swift do proaktywnego generowania każdej możliwej wersji display
, która może być wymagana przez niektórych przyszłych dzwoniących. Swift może poprawić się w czasie, ale myślę, że jest to trudny problem. (Swift już cierpi na pewne ograniczenia wydajności, aby specjaliści w dziedzinie medycyny publicznej mogli być wyspecjalizowani bez dostępu do oryginalnego kodu źródłowego, co sprawiłoby, że problem ten byłby jeszcze bardziej skomplikowany.)
OK, więc to rozumiemy. T
nie jest Printable
. Ale co by było, gdybyśmy mieli typ, który byłby jednoznacznie Printable
, który znaliśmy podczas kompilacji i żyliśmy wewnątrz tej funkcji? Czy to wtedy działa?
func display() {
if let p = args as? Printable {
print(Printer.print(p))
} else {
print(Printer.print(args))
}
}
No tak blisko ... ale nie do końca. To działa prawie. if-let
faktycznie robi dokładnie to, co chcesz. p
zostaje przypisany. Jest to Printable
. Ale nadal wywołuje funkcję nie do wydrukowania. ?!?!?!?!
To jest miejsce, w którym osobiście uważam, że Swift jest właśnie uszkodzony i ma duże nadzieje, że zostanie naprawiony. To może być nawet błąd. Problem polega na tym, że sama konfiguracja nie jest zgodna z Printable
. Tak, też tego nie rozumiem, ale proszę. Musimy więc zrobić coś, co ma zgodne z Printable
w celu uzyskania odpowiedniego przeciążenia. Jak zwykle, type erasers na ratunek.
struct AnyPrintable: Printable {
let value: Printable
}
struct Generic<T> {
var args: T
func display() {
if let p = args as? Printable {
print(Printer.print(AnyPrintable(value: p)))
} else {
print(Printer.print(args))
}
}
}
A to zostanie wydrukowane tak, jak chcesz. (Przy założeniu, że Printable
wymaga pewnych metod, można tylko dodać te metody do AnyPrintable
typu gumki.)
Oczywiście prawidłowa odpowiedź nie jest w użyciu przeciążeń generycznych w ten sposób w Printer
. To po prostu zbyt mylące i kruche. Wygląda tak ładnie, ale cały czas się wysadza.
Dziękuję bardzo za te informacje. Wydaje się skomplikowane, aby pasować do tego, co próbuję teraz zrobić. https://github.com/RahulKatariya/Reactofire/blob/develop/ReactofireTests/Models/GenericResponse/_GenericResponse.swift .. Próbuję przekazać testy GenericResponseString i GenericResponsePerson. –
Powodzenia ... Zrobiłem wiele eksperymentów w tego rodzaju super-sprytnych/magicznych podejściach do podstawowych problemów. Osobiście odkryłem, że kilka linii prostego kodu, nawet jeśli od czasu do czasu trzeba powtarzać kilka nużących linii tu i tam, działa znacznie lepiej, szczególnie gdy przychodzi czas na debugowanie (po prostu 'straży-let' mój parsowanie JSON teraz; o wiele łatwiej). To nie jest tak ekscytujące, ale w moich projektach działało o wiele lepiej. Ale powodzenia dla tych, którzy wciąż odkrywają granicę! To zabawne, ale jest tu dużo frustracji. –