Mam problem z następującym kodem:
func generic1<T>(name : String){
}
func generic2<T>(name : String){
generic1<T>(name)
}
wynik generic1 (nazwa) do błędu kompilatora „Nie można jawnie wyspecjalizować funkcji ogólnej”
Czy jest jakiś sposób, aby uniknąć tego błędu? Nie mogę zmienić sygnatury funkcji generic1, dlatego powinno to być (String) -> Void
T
w ogóle jest używany typ symbolu zastępczegogeneric1()
? Jak nazwałbyś tę funkcję, aby kompilator mógł wywnioskować typ?func foo<T>() -> T { ... }
robi coś z obiektemt
typuT
i wynikiemt
. Wyraźna specjalizacjaT
umożliwiłabyt1
ingerencjęvar t1 = foo<T>()
. Żałuję, że nie ma sposobu na tak nazywanie funkcji. C # na to pozwalaOdpowiedzi:
Miałem też ten problem i znalazłem obejście dla mojego przypadku.
W tym artykule autor ma ten sam problem
https://www.iphonelife.com/blog/31369/swift-programming-101-generics-practical-guide
Wydaje się więc, że problem polega na tym, że kompilator musi jakoś wywnioskować typ T. Ale nie wolno po prostu używać generycznego <type> (params ...).
Zwykle kompilator może szukać typu T, skanując typy parametrów, ponieważ w wielu przypadkach jest to miejsce, w którym T jest używany.
W moim przypadku było trochę inaczej, ponieważ typem zwracanym przez moją funkcję był T. W twoim przypadku wygląda na to, że w ogóle nie użyłeś T w swojej funkcji. Chyba właśnie uprościłeś przykładowy kod.
Mam więc następującą funkcję
func getProperty<T>( propertyID : String ) -> T
A w przypadku np
getProperty<Int>("countProperty")
kompilator podaje mi błąd:
Aby więc udostępnić kompilatorowi inne źródło informacji, z którego będzie mógł wywnioskować typ T, musisz jawnie zadeklarować typ zmiennej, w której zapisywana jest wartość zwracana.
var value : Int = getProperty("countProperty")
W ten sposób kompilator wie, że T musi być liczbą całkowitą.
Myślę więc, że ogólnie oznacza to po prostu, że jeśli określisz funkcję ogólną, musisz przynajmniej użyć T w typach parametrów lub jako typ zwracany.
źródło
func updateProperty<T>( propertyID : String )
let value = foo() as? Type
żeby można go było użyć w aif
lubguard
wynik jest opcjonalny, ale nie ...Szybki 5
Zwykle istnieje wiele sposobów definiowania funkcji ogólnych. Ale są one oparte na warunku, który
T
musi być używany jako aparameter
lub jakoreturn type
.extension UIViewController { class func doSomething<T: UIView>() -> T { return T() } class func doSomethingElse<T: UIView>(value: T) { // Note: value is a instance of T } class func doLastThing<T: UIView>(value: T.Type) { // Note: value is a MetaType of T } }
Po tym musimy podać
T
podczas rozmowy.let result = UIViewController.doSomething() as UIImageView // Define `T` by casting, as UIImageView let result: UILabel = UIViewController.doSomething() // Define `T` with property type, as UILabel UIViewController.doSomethingElse(value: UIButton()) // Define `T` with parameter type, as UIButton UIViewController.doLastThing(value: UITextView.self) // Define `T` with parameter type, as UITextView
Odniesienie:
źródło
self.result = UIViewController.doSomething()
działa samodzielnie, o ile wpisałeś właściwość podczas jej deklarowania.doLastThing<UITextView>()
staje się SwiftdoLastThing(UITextView.self)
, co nie jest ... przynajmniej najgorsze. Lepsze niż jawne wpisywanie skomplikowanych wyników. Dzięki za obejście.Rozwiązanie polega na przyjęciu typu klasy jako parametru (jak w Javie)
Aby kompilator wiedział, z jakim typem ma do czynienia, podaj klasę jako argument
extension UIViewController { func navigate<ControllerType: UIViewController>(_ dump: ControllerType.Type, id: String, before: ((ControllerType) -> Void)?){ let controller = self.storyboard?.instantiateViewController(withIdentifier: id) as! ControllerType before?(controller) self.navigationController?.pushViewController(controller, animated: true) } }
Zadzwoń jako:
self.navigate(UserDetailsViewController.self, id: "UserDetailsViewController", before: { controller in controller.user = self.notification.sender })
źródło
Nie potrzebujesz tutaj generycznego, ponieważ masz statyczne typy (String jako parametr), ale jeśli chcesz, aby funkcja ogólna wywołała inną, możesz wykonać następujące czynności.
Korzystanie z metod ogólnych
func fetchObjectOrCreate<T: NSManagedObject>(type: T.Type) -> T { if let existing = fetchExisting(type) { return existing } else { return createNew(type) } } func fetchExisting<T: NSManagedObject>(type: T.Type) -> T { let entityName = NSStringFromClass(type) // Run query for entiry } func createNew<T: NSManagedObject>(type: T.Type) -> T { let entityName = NSStringFromClass(type) // create entity with name }
Korzystanie z klasy ogólnej (mniej elastyczna, ponieważ rodzaj ogólny można zdefiniować tylko dla 1 typu na instancję)
class Foo<T> { func doStuff(text: String) -> T { return doOtherStuff(text) } func doOtherStuff(text: String) -> T { } } let foo = Foo<Int>() foo.doStuff("text")
źródło
Myślę, że kiedy określasz funkcję ogólną, powinieneś określić niektóre parametry typu T, jak następuje:
func generic1<T>(parameter: T) { println("OK") } func generic2<T>(parameter: T) { generic1(parameter) }
a jeśli chcesz wywołać metodę handle (), możesz to zrobić, pisząc protokół i określając ograniczenie typu dla T:
protocol Example { func handle() -> String } extension String: Example { func handle() -> String { return "OK" } } func generic1<T: Example>(parameter: T) { println(parameter.handle()) } func generic2<T: Example>(parameter: T) { generic1(parameter) }
więc możesz wywołać tę ogólną funkcję za pomocą String:
generic2("Some")
i będzie się kompilować
źródło
Jak dotąd, moja najlepsza praktyka wykorzystywała odpowiedź @ orkhan-alikhanov. Dzisiaj, patrząc na SwiftUI i jak
.modifier()
iViewModifier
implementację, znalazłem inny sposób (czy jest to bardziej obejście?)Po prostu zawiń drugą funkcję w plik
struct
.Przykład:
Jeśli ten daje Ci komunikat „Nie można jawnie wyspecjalizować funkcji ogólnej”
func generic2<T>(name: String){ generic1<T>(name) }
Ten może pomóc. Zawinąć deklarację
generic1
wstruct
:struct Generic1Struct<T> { func generic1(name: String) {## do, whatever it needs with T ##} }
i zadzwoń z:
func generic2<T>(name : String){ Generic1Struct<T>().generic1(name: name) }
Uwagi:
struct
jest dobrym przykładem. To obejście nie ma trochę więcej informacji - ale przekazuje kompilatorowi. Te same informacje, ale inne wyniki? Więc coś jest nie tak. Jeśli jest to błąd kompilatora, można go naprawić.źródło
Miałem podobny problem z funkcją klasy ogólnej
class func retrieveByKey<T: GrandLite>(key: String) -> T?
.Nie mógłbym tego nazwać,
let a = retrieveByKey<Categories>(key: "abc")
gdzie Kategorie jest podklasą GrandLite.let a = Categories.retrieveByKey(key:"abc")
zwrócił GrandLite, a nie Categories. Funkcje ogólne nie wnioskują o typie na podstawie klasy, która je wywołuje.class func retrieveByKey<T: GrandLite>(aType: T, key: String>) -> T?
dał mi błąd, gdy próbowałem,let a = Categories.retrieveByKey(aType: Categories, key: "abc")
dał mi błąd, że nie można przekonwertować Categories.Type na GrandLite, mimo że Categories jest podklasą GrandLite. JEDNAK...class func retrieveByKey<T: GrandLite>(aType: [T], key: String) -> T?
zrobił pracę gdybym próbowałlet a = Categories.retrieveByKey(aType: [Categories](), key: "abc")
widocznie wyraźne przypisanie podklasy nie działa, ale niejawna assigment przy użyciu innego typu rodzajowego (tablica) działa w Swift 3.źródło
aType
jako wystąpienieT
, zamiast stawiaćCategories
się. Nplet a = Categories.retrieveByKey(aType: Categories(), key: "abc")
. : . Innym rozwiązaniem jest zdefiniowanieaType: T.Type
. Następnie wywołaj metodę jakolet a = Categories.retrieveByKey(aType: Categories.self, key: "abc")