Dans le code suivant, je veux tester si x
est une SpecialController
. Si c'est le cas, je veux obtenir la currentValue
en tant que SpecialValue
. Comment est-ce que tu fais ça? Si ce n'est pas avec un casting, alors une autre technique.
La dernière ligne ne sera pas compilée. Il existe une erreur: Le protocole "SpecialController" ne peut être utilisé que comme contrainte générique car il a des exigences de type Self ou associé.
protocol SpecialController {
associatedtype SpecialValueType : SpecialValue
var currentValue: SpecialValueType? { get }
}
...
var x: AnyObject = ...
if let sc = x as? SpecialController { // does not compile
Malheureusement, Swift ne prend actuellement pas en charge l’utilisation de protocoles avec les types associés en tant que types réels. Ceci cependant est techniquement possible pour le compilateur; et il pourrait bien être implémenté dans une future version du langage .
Dans votre cas, une solution simple consiste à définir un «protocole parallèle» à partir duquel SpecialController
dérive et vous permet d'accéder à currentValue
via une exigence de protocole qui le type l'efface:
// 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!")
}
[Modifié pour corriger: : SpecialValue
, pas = SpecialValue
]
Ce n'est pas possible. SpecialValueController
est un "type incomplet" conceptuellement que le compilateur ne peut pas savoir. SpecialValueType
, bien qu'il soit contraint par SpecialValue
, il n'est pas connu jusqu'à ce qu'il soit déterminé par une classe adoptante. Il s’agit donc d’un espace réservé aux informations insuffisantes. as?
- ne peut pas être vérifié.
Vous pouvez avoir une classe de base qui adopte SpecialController
avec un concrete type pour SpecialValueController
et plusieurs classes enfant qui héritent de la classe adoptante, si vous recherchez toujours un degré de polymorphisme.
Cela ne fonctionne pas car SpecialController
n'est pas un type unique. Vous pouvez considérer les types associés comme une sorte de génériques. Un SpecialController
avec son SpecialValueType
étant un Int
est un type complètement différent d'un SpecialController
avec son SpecialValueType
étant un String
, tout comme Optional<Int>
est un type complètement différent de Optional<String>
.
De ce fait, cela n'a aucun sens de transtyper en SpecialValueType
, car cela masquerait le type associé et vous permettrait d'utiliser (par exemple) un SpecialController
avec son SpecialValueType
étant un Int
avec un SpecialController
avec son SpecialValueType
String
est attendu.
Comme le suggère le compilateur, SpecialController
ne peut être utilisé que comme contrainte générique. Vous pouvez avoir une fonction générique sur T
, avec la contrainte que T
doit être un SpecialController
. Le domaine T
couvre désormais tous les types concrets de SpecialController
, par exemple un domaine associé à Int
et un type String
. Pour chaque type associé possible, il existe un SpecialController
distinct et, par extension, un T
.
Pour approfondir l'analogie Optional<T>
. Imaginez si ce que vous essayez de faire était possible. Ce serait un peu comme ceci:
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.