2016-08-17 22 views
15

chciałbym zwracają UIViewController zgodny MyProtocol od sposobu, więc używam podpis metoda:rodzajowych w Swift - „Generic parametr«T»nie można wywnioskować

func myMethod<T where T : UIViewController, T : MyProtocol>() -> T { 

Pierwszą rzeczą nie rozumiem: jeśli myMethod powraca np MyViewController który musi po podpis, muszę zmusić oddanych go:

class MyViewController: UIViewController, MyProtocol 

nie mogę po prostu return MyViewController() ale muszę oddać go tak: return MyViewController() as! T - dlaczego jest to konieczność y?

A druga rzecz: jak mogę gdzieś skorzystać z tej metody? Nie mogę po prostu powiedzieć

let x = myMethod() as? UIViewController 

jak pojawia się błąd

Generic parameter 'T' could not be inferred 

Jak mogę osiągnąć coś takiego? Jeśli wyrzucę go do MyViewController, to działa, ale chciałbym tego uniknąć.

EDIT: Przykład

class MyViewController : UIViewController, MyProtocol { 
} 

protocol MyProtocol { 
} 

func myMethod<T>() -> T where T : UIViewController, T : MyProtocol { 
    return MyViewController() as! T // why is the cast necessary? 
} 

ok, mam dostać jedną część, ale dlaczego jest obsada do T konieczne? MyViewController jest podklasą UIViewController i jest zgodna z protokołem, więc nie należy wykonywać rzutowania, prawda?

+0

Co dokładnie próbujesz osiągnąć? Dlaczego w ogóle używasz generycznych? – Alexander

+0

Chciałbym pracować z typem, który jest 'UIViewController' i jest zgodny z określonym protokołem; Mam różne klasy, które są zgodne z tymi zasadami, dlatego nie chcę używać określonego typu. – swalkner

+0

Nie chcesz zaakceptować odpowiedzi? – Honey

Odpowiedz

17
func myMethod<T where T : UIViewController, T : MyProtocol>() -> T 

Deklaracja ta mówi: istnieje funkcja o nazwie myMethod, tak że myMethod powraca kilka specyficznyT gdzie T jest podtypem UIViewController a także MyProtocol. Nie oznacza to, jaki typ faktycznie jest typu T i nie mówi, że istnieje tylko jeden taki myMethod. Może być ich wiele, jeśli istnieje wiele typów, które są podklasami UIViewController i są zgodne z MyProtocol. Każdy z tych typów tworzy nową wersję myMethod (tak naprawdę nowe rozwiązanie stwierdzenia myMethod powoduje, że taka funkcja istnieje).

To nie jest to samo, co:

func myMethod() -> UIViewController 

To mówi: Funkcja myMethod zwraca którykolwiek z podtypów UIViewController.

W Swift nie ma sposobu na wyrażenie "dowolnego typu, który jest podklasą UIViewController i jest podtypem MyProtocol." Możesz omówić tylko określony typ, który spełnia to kryterium. Swift nie może w ten sposób łączyć klas i protokołów; to tylko aktualne ograniczenie języka, a nie głęboki problem z projektowaniem.

Specyficzny versus any jest problemem. Istnieje wiele funkcji, które spełniają Twoją deklarację myMethod. Każdy, który można podłączyć, zgodny z regułami, byłby kandydatem. Więc kiedy mówisz myMethod(), kompilator nie wie, co konkretnie masz na myśli.

(miałem zamiar rozszerzać tę odpowiedź dostarczyć je w krótszym typu teorii, bardziej „jak można to zrobić w kod” chodzi, ale donnywals ma już doskonałą wersję tego.)

* aby edytowanym pytanie *

func myMethod<T>() -> T where T : UIViewController, T : MyProtocol { 
    return MyViewController() as! T // why is the cast necessary? 
} 

T jest specyficzny typ postanowił przez rozmówcę. Nie jest to "dowolny typ, który się zgadza" jest "konkretny, konkretny typ, który jest zgodny". Rozważmy przypadek, że o nazwie:

let vc: SomeOtherViewController = myMethod() 

W tym przypadku T jest SomeOtherViewController. MyViewController nie jest tego typu, więc to, co robisz z rzutowaniem as!, jest niebezpieczne.

8

W taki sposób, zwracając T oznacza, że ​​musisz zwrócić T. Jeśli zwrócisz MyViewController, typem powrotu powinno być MyViewController. T jest typem ogólnym, który przyjmie postać jakiejkolwiek kompilacji Swift, którą można wywnioskować.

Tak więc, z podpisem metody, prosta implementacja protokołu i metody może wyglądać tak.

protocol MyProtocol { 
    var name: String { get set } 
} 

func myMethod<T where T : UIViewController, T : MyProtocol>() -> T { 
    var vc = T() 
    vc.name = "Hello, world" 
    return vc 
} 

więc, biorąc pod uwagę twój przykład wykorzystania:

let x = myMethod() 

Skąd kompilator wie, co konkretny rodzaj T jest? Nie ma nic, co dałoby mu podpowiedź: MyViewController. Jedyne, co wiemy, to to, że niezależnie od tego, co jest T, powinno być MyViewController lub jego podklasa. I powinien być zgodny z MyProtocol. Ale to nie dostarcza informacji o tym, jaki powinien być typ T.

Jedynym miejscem, w którym kompilator może wywnioskować, co chcemy, aby T było przez wartość zwracaną. Cały kod między <> jest ograniczeniami dla tego, co może być dozwolone. -> T to jedyne miejsce, w którym T jest widoczny poza ograniczeniami. Jeśli więc w jakiś sposób możemy powiedzieć kompilatorowi, co chcemy, aby zwrócić, aby uzyskać myMethod, przekazaliśmy mu wystarczającą ilość informacji, aby wywnioskować, że jest to T.

Twój typecast działa, ale zgadzam się, że to nie jest piękne. Dużo ładniejszy sposób, aby kompilator wywnioskował, że jest to T.

let vc: MyViewController = myMethod() 

Określając rodzaj vc, kompilator wie, że chcemy myMethod zwrócić MyViewController. Tak więc teraz typ T można wywnioskować i jeśli wrócimy T, faktycznie wrócimy MyViewController.

+0

wielkie dzięki; jeszcze jedno pytanie, zobacz moją edycję – swalkner

+0

@donnywals, jak na temat ogólnych parametrów metody, a nie typem zwrotu? Jak wywnioskować coś takiego? – Axel

Powiązane problemy