次のコードに問題があります:
func generic1<T>(name : String){
}
func generic2<T>(name : String){
generic1<T>(name)
}
generic1(名)コンパイラエラーの結果は、「明示的に汎用的な機能を特化することはできません」
このエラーを回避する方法はありますか?generic1関数のシグネチャを変更できないため、(String)-> Voidにする必要があります
次のコードに問題があります:
func generic1<T>(name : String){
}
func generic2<T>(name : String){
generic1<T>(name)
}
generic1(名)コンパイラエラーの結果は、「明示的に汎用的な機能を特化することはできません」
このエラーを回避する方法はありますか?generic1関数のシグネチャを変更できないため、(String)-> Voidにする必要があります
T
使用していgeneric1()
ますか?コンパイラが型を推測できるように、その関数をどのように呼び出しますか?
func foo<T>() -> T { ... }
オブジェクトとreturn t
で何かを行います。明示的に特化すると、で推論できるようになります。これらの方法で関数を呼び出す方法があったらいいのにと思います。C#で許可T
t
T
t1
var t1 = foo<T>()
回答:
私もこの問題を抱えており、私のケースの回避策を見つけました。
この記事では著者にも同じ問題があります
https://www.iphonelife.com/blog/31369/swift-programming-101-generics-practical-guide
したがって、問題はコンパイラがTの型をどういうわけか推測する必要があることのようです。ただし、単純にgeneric <type>(params ...)を使用することはできません。
通常、ここではTが使用されるため、コンパイラーはパラメーターの型をスキャンすることにより、Tの型を探すことができます。
私の場合、関数の戻り値の型がTだったため、少し異なりました。あなたの場合、関数でTをまったく使用していないようです。サンプルコードを単純化したと思います。
だから私は次の機能を持っています
func getProperty<T>( propertyID : String ) -> T
そして、例えば、
getProperty<Int>("countProperty")
コンパイラは私にエラーを出します:
ジェネリック関数を明示的に特化することはできません
したがって、Tの型を推測するための別の情報源をコンパイラーに提供するには、戻り値が保存される変数の型を明示的に宣言する必要があります。
var value : Int = getProperty("countProperty")
このようにして、コンパイラーはTが整数でなければならないことを認識します。
つまり、全体的には、ジェネリック関数を指定する場合、少なくともパラメーターの型でTを使用するか、戻り値の型として使用する必要があるということです。
func updateProperty<T>( propertyID : String )
let value = foo() as? Type
ようにして、それをで使用できるようにするif
かguard
、結果をオプションにすることはできますが、そうではありません...
一般的に、ジェネリック関数を定義するには多くの方法があります。ただし、これらは、またはとしてT
使用する必要がある条件に基づいています。parameter
return 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
}
}
その後、T
電話時に提供する必要があります。
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
参照:
self.result = UIViewController.doSomething()
プロパティを宣言するときにプロパティを入力している限り、それ自体で動作することにも気付きました。
doLastThing<UITextView>()
はSwift doLastThing(UITextView.self)
になります。少なくとも、最悪ではありません。複雑な結果を明示的に入力するよりも優れています。回避策をありがとう。
コンパイラーが処理している型をコンパイラーに知らせるには、クラスを引数として渡します
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)
}
}
次のように呼び出す:
self.navigate(UserDetailsViewController.self, id: "UserDetailsViewController", before: {
controller in
controller.user = self.notification.sender
})
静的型(パラメーターとしての文字列)があるため、ここではジェネリックは必要ありませんが、ジェネリック関数を別の関数で呼び出したい場合は、次のようにします。
ジェネリックメソッドの使用
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
}
ジェネリッククラスを使用する(ジェネリックはインスタンスごとに1つのタイプに対してのみ定義できるため、柔軟性は低くなります)
class Foo<T> {
func doStuff(text: String) -> T {
return doOtherStuff(text)
}
func doOtherStuff(text: String) -> T {
}
}
let foo = Foo<Int>()
foo.doStuff("text")
ジェネリック関数を指定するときは、次のようなT型のパラメーターのいくつかを指定する必要があると思います。
func generic1<T>(parameter: T) {
println("OK")
}
func generic2<T>(parameter: T) {
generic1(parameter)
}
また、handle()メソッドを呼び出す場合は、プロトコルを記述し、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)
}
したがって、この汎用関数をStringで呼び出すことができます。
generic2("Some")
そしてそれはコンパイルされます
これまでのところ、私の個人的なベストプラクティスでは、@ orkhan-alikhanovの回答を使用しました。今日、SwiftUIを見て、どのようにする場合.modifier()
とViewModifier
実装されて、私は別の方法を発見した(または、より回避策です?)
2番目の関数をにラップするだけstruct
です。
これが「ジェネリック関数を明示的に特化することはできません」とあなたに与えるなら
func generic2<T>(name: String){
generic1<T>(name)
}
これは役立つかもしれません。の宣言をgeneric1
にラップしますstruct
。
struct Generic1Struct<T> {
func generic1(name: String) {## do, whatever it needs with T ##}
}
そしてそれを呼び出す:
func generic2<T>(name : String){
Generic1Struct<T>().generic1(name: name)
}
struct
は良い例です。ここでの回避策には少し詳しい情報はありませんが、コンパイラに合格します。同じ情報ですが、結果は異なりますか?その後、何かがおかしい。コンパイラのバグの場合は修正できます。ジェネリッククラス関数にも同様の問題がありましたclass func retrieveByKey<T: GrandLite>(key: String) -> T?
。
let a = retrieveByKey<Categories>(key: "abc")
CategoriesがGrandLiteのサブクラスであるとは言えません。
let a = Categories.retrieveByKey(key:"abc")
カテゴリではなくGrandLiteを返しました。ジェネリック関数は、それらを呼び出すクラスに基づいて型を推測しません。
class func retrieveByKey<T: GrandLite>(aType: T, key: String>) -> T?
私がしようとしたときに私にエラーを与えlet a = Categories.retrieveByKey(aType: Categories, key: "abc")
てくれ、それはカテゴリーがGrandLiteのサブクラスであっても、GrandLiteにCategories.Typeを変換することができなかったというエラーが発生しました。しかしながら...
class func retrieveByKey<T: GrandLite>(aType: [T], key: String) -> T?
やった、私がしようとした場合、作業をlet a = Categories.retrieveByKey(aType: [Categories](), key: "abc")
明らかに動作しないサブクラスの明示的な割り当てを、しかし、別のジェネリック型(配列)を使用して、暗黙のassigmentはスウィフト3で作業を行います。
aType
でT
はなく、のインスタンスとして提供する必要がありますCategories
。例:let a = Categories.retrieveByKey(aType: Categories(), key: "abc")
。別の解決策はdefine aType: T.Type
です。次に、メソッドを次のように呼び出しますlet a = Categories.retrieveByKey(aType: Categories.self, key: "abc")