protocol ParentProtocol { }
protocol ChildProtocol: ParentProtocol { }
protocol Child_With_Value_Protocol: ParentProtocol {
associatedType Value
func retrieveValue() -> Value
}
と の両方をParentProtocol含む型の 1 つの配列を作成しようとしています。異種配列をループし、 type だけの値を返す関数を作成する方法はありますか? ChildProtocolChild_With_Value_ProtocolChild_With_Value_Protocol
これには、アーキテクチャの変更が必要になる場合があります。すべてのソリューションに開かれています。
試みられた失敗した解決策 #1
var parents: [ParentProtocol] = [...both ChildProtocol & Child_With_Value_Protocol...]
func retrieveValues() -> [Any] {
var values = [Any]()
for parent in parents {
if let childWithValue = parent as? Child_With_Value_Protocol { // Fails to compile
values.append(childWithValue.retrieveValue())
}
}
return values
}
protocol 'Child_With_Value_Protocol' can only be used as a generic constraint because it has Self or associated type requirementsこれは、単に に変換されたときにコンパイラが型を認識しないため、理にかなっている のエラーでChild_With_Value_Protocol失敗します。これは、次の失敗したソリューションにつながります。
試みられた失敗した解決策 #2
配列が だけの同種配列である場合Child_With_Value_Protocol、型消去を使用して値を取得できます。
var parents: [ParentProtocol] = [...both ChildProtocol & Child_With_Value_Protocol...]
struct AnyValue {
init<T: Child_With_Value_Protocol>(_ protocol: T) {
_retrieveValue = protocol.retrieveValue as () -> Any
}
func retrieveValue() -> Any { return _retrieveValue() }
let _retrieveValue: () -> Any
}
func retrieveValues() -> [Any] {
var values = [Any]()
for parent in parents {
values.append(AnyValue(parent).retrieveValue()) // Fails to compile
}
return values
}
これは、構造体AnyValueに の初期化子がないため、コンパイルに失敗しますParentProtocol。
試みられた失敗した解決策 #3
struct AnyValue {
init<T: Child_With_Value_Protocol>(_ protocol: T) {
_retrieveValue = protocol.retrieveValue as () -> Any
}
func retrieveValue() -> Any { return _retrieveValue() }
let _retrieveValue: () -> Any
}
var erased: [AnyValue] = [AnyValue(...), AnyValue(...), AnyValue(...)]
func retrieveValues() -> [Any] {
var values = [Any]()
for value in erased {
values.append(value.retrieveValue())
}
return values
}
他のソリューションとは異なり、このソリューションは実際にコンパイルされます。erasedこのソリューションの問題は、配列が の型消去されたバージョンの値しか保持できないという事実にありChild_With_Value_Protocolます。目標は、配列が と の両方の型を保持することです 。Child_With_Value_ProtocolChildProtocol
試みられた失敗した解決策 #4
type-erase 構造体を変更してイニシャライザを含めると、ParentProtocolコンパイルされるソリューションが作成されますが、構造体はより具体的な init ではなく、より具体的でない init のみを使用します。
struct AnyValue {
init?<T: ParentProtocol>(_ protocol: T) {
return nil
}
init?<T: Child_With_Value_Protocol>(_ protocol: T) {
_retrieveValue = protocol.retrieveValue as () -> Any
}
func retrieveValue() -> Any { return _retrieveValue() }
let _retrieveValue: (() -> Any)?
}