2016-02-16 14 views
7

Istnieje protokół drukowania i drukarka strukturalna od strony trzeciej.Ogólne nadpisanie metody nie działa w trybie błyskawicznym.

protocol Printable {} 

struct Printer { 

    static func print<T>(object: T) -> String { 
     return "T" 
    } 

    static func print<T: Printable>(object: T) -> String { 
     return "Printable" 
    } 

} 

Teraz Robię rodzajowe

struct Generic<T> { 

    var args: T 

    func display() { 
     print(Printer.print(args)) 
    } 

} 

i dwa konstrukcjom

struct Obj {} 
struct PrintableObj: Printable {} 
var obj = Generic(args: Obj()) 
var printableObj = Generic(args: PrintableObj()) 

gdy zgłoszę funkcji wyświetlanych na obu z nich.

obj.display() 

wyświetlacze T

printableObj.display() 

wyświetlacze T ale chcę go wydrukować "do druku"

Jednym z rozwiązań mogę pomyśleć, to mając dwa różne leki generyczne

struct Generic<T> 
struct PrintableGeneric<T: Printable> 

Czy istnieje inne rozwiązanie bez zmiany opcji Printable p.? rotocol i Printer struct.

Odpowiedz

1

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.

+0

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. –

+0

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. –

1

Moim zdaniem jedyną opcją masz - jest użycie if-else z typu odlewania w "print()" Funkcja

static func print<T>(object: T) -> String { 
    if let _ = object as? Printable { 
     return "Printable" 
    } 
    return "T" 
} 

lub nierodzajową wariant

static func print(object: Any) -> String { 
    if let _ = object as? Printable { 
     return "Printable" 
    } 
    return "T" 
} 
+0

Dzięki. Ale ten kod pochodzi z innego źródła i nie mogę go zmodyfikować. Mogę tylko wprowadzać zmiany w Generic. –

2
static func print<T>(object: T) -> String { 
    if object is Printable { 
     return "Printable" 
    } else { 
     return "T" 
    } 
} 
+0

Dzięki. Ale ten kod pochodzi z innego źródła i nie mogę go zmodyfikować. Mogę tylko wprowadzać zmiany w Generic. –

Powiązane problemy