0
 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)?
}
4

1 に答える 1

0

以前のコメントはおそらく正しいです。それでも、バリアントを列挙型にボックス化し、それらの配列を作成することはできます。次に、参照は列挙値をオンにし、それぞれに適切なタイプの関連データがあります

編集: 質問されている質問とは関係がないように思われるため、associatedValue は気にしませんでした。以下はプレイグラウンドで機能します。

protocol ParentProtocol: CustomStringConvertible {
    static func retrieveValues(parents: [FamilyBox]) -> [ParentProtocol]
}

protocol ChildProtocol: ParentProtocol { }

protocol Other_Child_Protocol: ParentProtocol { }

enum FamilyBox {
    case Parent(parent: ParentProtocol)
    case Child(child: ChildProtocol)
    case OtherChildProtocol(withValue: Other_Child_Protocol)

}


var parents: [FamilyBox] = []

struct P: ParentProtocol {
    var description: String { return "Parent" }

    static func retrieveValues(parents: [FamilyBox]) -> [ParentProtocol] {
        var values = [ParentProtocol]()

        for parent in parents {
            switch parent {
            case .Parent(let elementValue):
                values.append(elementValue)
            default:
                break;
            }
        }
        return values
    }
}

struct C: ChildProtocol {
    var description: String { return "Child" }

    static func retrieveValues(parents: [FamilyBox]) -> [ParentProtocol] {
        var values = [ParentProtocol]()

        for parent in parents {
            switch parent {
            case .Child(let elementValue):
                values.append(elementValue)
            default:
                break;
            }
        }
        return values
    }
}

struct CV: Other_Child_Protocol {
    var description: String { return "Other Child" }

    static func retrieveValues(parents: [FamilyBox]) -> [ParentProtocol] {
        var values = [ParentProtocol]()

        for parent in parents {
            switch parent {
            case .OtherChildProtocol(let elementValue):
                values.append(elementValue)
            default:
                break;
            }
        }
        return values
    }
}

let p = FamilyBox.Parent(parent: P())
let c = FamilyBox.Child(child: C())
let cv = FamilyBox.OtherChildProtocol(withValue: CV())
let array:[FamilyBox] = [p, c, cv]

print(P.retrieveValues(array))
print(C.retrieveValues(array))
print(CV.retrieveValues(array))

最後の 3 行からの出力は次のとおりです。

[Parent]
[Child]
[Other Child]

改善できると確信していますが、それは当初の意図を満たしていると思います。いいえ?

于 2016-07-03T11:54:00.580 に答える