web-dev-qa-db-ja.com

Swiftで、関連するタイプのプロトコルにキャストする方法は?

次のコードでは、xSpecialControllerかどうかをテストします。そうである場合、currentValueSpecialValueとして取得します。これどうやってやるの?キャストでない場合は、他のテクニックを使用します。

そこの最後の行はコンパイルされません。エラーがあります:Selfまたは関連する型の要件があるため、プロトコル「SpecialController」は一般的な制約としてのみ使用できます。

protocol SpecialController {
    associatedtype SpecialValueType : SpecialValue
    var currentValue: SpecialValueType? { get }
}
...
var x: AnyObject = ...
if let sc = x as? SpecialController {  // does not compile
21
Rob N

残念ながら、Swiftは現在、実際の型として関連付けられた型を持つプロトコルの使用をサポートしていません。ただし、これは 技術的に可能 コンパイラが行うことであり、- 言語の将来のバージョンで実装されるかもしれません

あなたの場合の簡単な解決策は、SpecialControllerの派生元である「シャドウプロトコル」を定義し、タイプがそれを消去するというプロトコル要件を通じてcurrentValueにアクセスできるようにすることです。

// This assumes SpecialValue doesn't have associated types – if it does, you can
// repeat the same logic by adding TypeErasedSpecialValue, and then using that.
protocol SpecialValue {
  // ...
}

protocol TypeErasedSpecialController {
  var typeErasedCurrentValue: SpecialValue? { get }
}

protocol SpecialController : TypeErasedSpecialController {
  associatedtype SpecialValueType : SpecialValue
  var currentValue: SpecialValueType? { get }
}

extension SpecialController {
  var typeErasedCurrentValue: SpecialValue? { return currentValue }
}

extension String : SpecialValue {}

struct S : SpecialController {
  var currentValue: String?
}

var x: Any = S(currentValue: "Hello World!")
if let sc = x as? TypeErasedSpecialController {
  print(sc.typeErasedCurrentValue as Any) // Optional("Hello World!")
}
16
Hamish

[修正のために編集:: SpecialValueではなく= SpecialValue]

これは不可能です。 SpecialValueControllerは概念的には「不完全な型」であり、コンパイラーはこれを知ることができません。 SpecialValueType、それはによって制約されるSpecialValueですが、採用しているクラスによって決定されるまで不明です。したがって、これは情報が不十分な本当にプレースホルダーです。 as?- nessを確認できません。

SpecialControllerの-​​concreteタイプを使用してSpecialValueControllerを採用する基本クラスがあり、採用しているクラスから継承する複数の子クラスがある場合は、ある程度のポリモーフィズムを求める。

1
BaseZen

SpecialControllerは単一の型ではないため、これは機能しません。関連する型は、ジェネリックの一種と考えることができます。 SpecialControllerSpecialValueTypeであるIntは、SpecialControllerSpecialValueTypeであるStringとは完全に異なる型です。これは、Optional<Int>Optional<String>とは完全に異なる型であるようにです。

このため、SpecialValueTypeにキャストしても意味がありません。これは、関連する型を覆い隠し、(たとえば)SpecialControllerSpecialValueTypeIntにして、SpecialControllerSpecialValueTypeにして、 Stringが必要です。

コンパイラーが示唆するように、SpecialControllerを使用できる唯一の方法は、一般的な制約としてです。 TTでなければならないという制約のもとで、SpecialControllerのジェネリック関数を使用できます。 Tのドメインは、SpecialControllerに関連付けられたタイプやIntを持つタイプなど、Stringのさまざまな具象タイプすべてに及ぶようになりました。関連付けられている可能性のある型ごとに、別個のSpecialControllerがあり、拡張すると、別個のTがあります。

Optional<T>のアナロジーをさらに引き出すため。あなたがしようとしていることが可能だったと想像してみてください。次のようになります。

func funcThatExpectsIntOptional(_: Int?) {}

let x: Optional<String> = "An optional string"
// Without its generic type parameter, this is an incomplete type. suppose this were valid
let y = x as! Optional
funcThatExpectsIntOptional(y) // boom.