私はSwinjectを使っていましたが、問題が発生します。私はほぼ一日中これに行き詰まっています。これはSwiftが静的に型付けされた言語であるためと思われますが、完全にはわかりません。
私の問題をこの遊び場でまとめました
protocol Protocol {}
class Class: Protocol {}
let test: Protocol.Type = Class.self
func printType(confromingClassType: Protocol.Type) {
print(confromingClassType)
}
func printType<Service>(serviceType: Service.Type) {
print(serviceType)
}
print(Class.self) // "Class"
printType(serviceType: Class.self) // "Class"
print(test) // "Class"
printType(confromingClassType: test) // "Class"
printType(serviceType: test) // "note: expected an argument list of type '(serviceType: Service.Type)'"
Test.selfやtype(of:test)などのさまざまなソリューションを試しましたが、どれも機能しません。
だから、変数として提供されたジェネリックパラメーターで関数を呼び出すことはできないと思いますか?
P.Type
_対_P.Protocol
_プロトコルのメタタイプには2種類あります。一部のプロトコルP
および準拠するタイプC
の場合:
P.Protocol
_は、プロトコル自体のタイプを示します(保持できる値は_P.self
_のみです)。P.Type
_は、プロトコルに準拠する具象型を示します。 _C.self
_の値を保持できますが、not_P.self
_ プロトコルが自分自身に準拠していないため (ただし、1つの例外があります) Any
は トップタイプ であるため、このルールはAny
です。したがって、メタタイプ値は_Any.Type
_として入力できます。_Any.self
_を含む)。あなたが直面している問題は、特定の汎用プレースホルダーT
に対して、T
がプロトコルP
の場合、_T.Type
_はであるということです。 not_P.Type
_ – _P.Protocol
_です。
したがって、例に戻ると、
_protocol P {}
class C : P {}
func printType<T>(serviceType: T.Type) {
print(serviceType)
}
let test: P.Type = C.self
// Cannot invoke 'printType' with an argument list of type '(serviceType: P.Type)'
printType(serviceType: test)
_
printType(serviceType:)
の引数としてtest
を渡すことはできません。どうして? test
は_P.Type
_なので、また、T
の代わりに、_serviceType:
_パラメータに_P.Type
_を指定させることはできません。
P
をT
に置き換えた場合、パラメーターは_P.Protocol
_を取ります。
_printType(serviceType: P.self) // fine, P.self is of type P.Protocol, not P.Type
_
T
のように、C
をコンクリート型に置き換えた場合、パラメーターは_C.Type
_を取ります。
_printType(serviceType: C.self) // C.self is of type C.Type
_
さて、T
をconcrete型に置き換えることができれば、_C.Type
_を関数に渡すことができることを学びました。 _P.Type
_がラップする動的な型で置き換えることはできますか?残念ながら、これには opening existentials と呼ばれる言語機能が必要です。これは現在、ユーザーが直接使用することはできません。
ただし、Swiftdoesは、プロトコル型のインスタンスまたはメタタイプのメンバーにアクセスするときに、存在を暗黙的に開きます(つまり、ランタイム型を掘り出し、汎用的なプレースホルダーの形でアクセスできます。プロトコル拡張でこの事実を利用できます。
_protocol P {}
class C : P {}
func printType<T>(serviceType: T.Type) {
print("T.self = \(T.self)")
print("serviceType = \(serviceType)")
}
extension P {
static func callPrintType/*<Self : P>*/(/*_ self: Self.Type*/) {
printType(serviceType: self)
}
}
let test: P.Type = C.self
test.callPrintType()
// T.self = C
// serviceType = C
_
ここではかなりのことが行われているので、少し解凍してみましょう。
P
の拡張メンバーcallPrintType()
には、Self
に制限された暗黙的な汎用プレースホルダーP
があります。暗黙のself
パラメーターは、このプレースホルダーを使用して入力されます。
_P.Type
_でcallPrintType()
を呼び出すと、Swiftは_P.Type
_がラップする動的タイプを暗黙的に掘り出します(これは存在の始まりです) 、それを使用してSelf
プレースホルダーを満たします。次に、この動的メタタイプを暗黙のself
パラメータに渡します。
したがって、Self
はC
によって満たされ、printType
の汎用プレースホルダーT
に転送できます。
T.Type
_のときに_P.Type
_が_T == P
_ではないのはなぜですか?汎用プレースホルダーP
の代わりにT
を使用しないため、上記の回避策がどのように機能するかがわかります。しかし、プロトコルタイプP
をT
に置き換えると、_T.Type
_でない_P.Type
_になるのはなぜですか?
まあ、考慮してください:
_func foo<T>(_: T.Type) {
let t: T.Type = T.self
print(t)
}
_
P
をT
に置き換えた場合はどうなりますか? _T.Type
_が_P.Type
_の場合、次のようになります。
_func foo(_: P.Type) {
// Cannot convert value of type 'P.Protocol' to specified type 'P.Type'
let p: P.Type = P.self
print(p)
}
_
これは違法です。 _P.self
_ではなく_P.Type
_型であるため、_P.Protocol
_を_P.Type
_に割り当てることはできません。
したがって、結果として、P
に準拠するany具象型を説明するメタタイプを取る関数パラメーターが必要な場合は、(1つの特定の具象適合型ではなく) )–ジェネリックではなく、_P.Type
_パラメータのみが必要です。ジェネリックスは、異種の型をモデル化しません。それがプロトコル型の目的です。
そして、それはまさにあなたがprintType(conformingClassType:)
で持っているものです:
_func printType(conformingClassType: P.Type) {
print(conformingClassType)
}
printType(conformingClassType: test) // okay
_
_P.Type
_型のパラメーターがあるため、test
を渡すことができます。ただし、これは_P.self
_型ではないため、_P.Type
_を渡すことができないことを意味します。
_// Cannot convert value of type 'P.Protocol' to expected argument type 'P.Type'
printType(conformingClassType: P.self)
_
私はあなたのコードを遊び場で実行しました、そしてそれがそれがコンパイルしない理由です
let test: Protocol.Type = Class.self
test
の型宣言を削除すると、コードが機能し、Class.Type
が15
行に出力されます。
したがって、次のコードはコンパイルして実行されます。
protocol Protocol {}
class Class: Protocol {}
let test = Class.self
func printType<Service>(serviceType: Service.Type) {
print(serviceType)
}
print(Class.Type.self) // "Class.Type"
printType(serviceType: Class.Type.self) // "Class.Type"
print(type(of: test)) // "Class.Type"
printType(serviceType: type(of: test)) // "Class.Type"
これが問題の解決に役立つことを願っています。
編集
元のコードでプレイグラウンドで発生しているエラーは次のとおりです。
Playground execution failed: error: Untitled Page 2.xcplaygroundpage:9:1: error: cannot invoke 'printType' with an argument list of type '(serviceType: Protocol.Type.Type)'
printType(serviceType: type(of: test)) // "Class.Type"
これは、Type
を2回呼び出すことを意味します。これが、コードがコンパイルされない理由です。これは、タイプProtocol.Type
の引数でメソッドを既に呼び出しているためです。
メソッドのシグネチャを次のように変更すると、
テストを許可:Protocol.Type = Class.self
func printType<Service>(serviceType: Service) {
print(serviceType)
}
すべてがコンパイルされて正しく動作し、Class.type
を出力します
test
が.Type
を一度しか呼び出せないため、これが私の最初のバージョンの回答をコンパイルする理由でもあります。