In the following code, I want to test if x
is a SpecialController
. If it is, I want to get the currentValue
as a SpecialValue
. How do you do this? If not with a cast, then some other technique.
在下面的代码中,我想测试x是否是SpecialController。如果是,我想将currentValue作为SpecialValue。你怎么做到这一点?如果没有演员,那么其他一些技巧。
The last line there won't compile. There error is: Protocol "SpecialController" can only be used as a generic constraint because it has Self or associated type requirements.
最后一行不会编译。错误是:协议“SpecialController”只能用作通用约束,因为它具有Self或关联类型要求。
protocol SpecialController {
associatedtype SpecialValueType : SpecialValue
var currentValue: SpecialValueType? { get }
}
...
var x: AnyObject = ...
if let sc = x as? SpecialController { // does not compile
3 个解决方案
#1
3
Unfortunately, Swift doesn't currently support the use of protocols with associated types as actual types. This however is technically possible for the compiler to do; and it may well be implemented in a future version of the language.
不幸的是,Swift目前不支持使用具有相关类型的协议作为实际类型。然而,编译器在技术上可以这样做;它可能会在未来的语言版本中实现。
A simple solution in your case is to define a 'shadow protocol' that SpecialController
derives from, and allows you to access currentValue
through a protocol requirement that type erases it:
在您的情况下,一个简单的解决方案是定义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!")
}
#2
1
[Edited to fix: : SpecialValue
, not = SpecialValue
]
[编辑修复:: SpecialValue,not = SpecialValue]
This is not possible. SpecialValueController
is an "incomplete type" conceptually so the compiler cannot know. SpecialValueType
, although it is constrained by SpecialValue
, it is not known until it is determined by any adopting class. So it is a really placeholder with inadequate information. as?
-ness cannot be checked.
这不可能。 SpecialValueController在概念上是一个“不完整类型”,因此编译器无法知道。 SpecialValueType虽然受到SpecialValue的约束,但在任何采用类确定之前都不知道。所以它是一个真正的占位符,信息不足。因为? - 无法检查。
You could have a base class that adopts SpecialController
with a concrete type for SpecialValueController
, and have multiple child classes that inherit from the adopting class, if you're still seeking a degree of polymorphism.
你可以有一个基类,它采用SpecialController和SpecialValueController的具体类型,并且有多个继承自采用类的子类,如果你仍然在寻求一定程度的多态性。
#3
0
This doesn't work because SpecialController
isn't a single type. You can think of associated types as a kind of generics. A SpecialController
with its SpecialValueType
being an Int
is a completely different type from a SpecialController
with its SpecialValueType
being an String
, just like how Optional<Int>
is a completely different type from Optional<String>
.
这不起作用,因为SpecialController不是单一类型。您可以将关联类型视为一种泛型。 SpecialValueType为Int的SpecialController是一个与SpecialController完全不同的类型,其SpecialValueType是一个String,就像Optional
Because of this, it doesn't make any sense to cast to SpecialValueType
, because that would gloss over the associated type, and allow you to use (for example) a SpecialController
with its SpecialValueType
being an Int
where a SpecialController
with its SpecialValueType
being a String
is expected.
因此,转换为SpecialValueType没有任何意义,因为这会掩盖相关类型,并允许您使用(例如)SpecialController,其SpecialValueType为Int,其中SpecialController的SpecialValueType为字符串是预期的。
As compiler suggests, the only way SpecialController
can be used is as a generic constraint. You can have a function that's generic over T
, with the constraint that T
must be a SpecialController
. The domain of T
now spans all the various concrete types of SpecialController
, such as one with an Int
associated type, and one with a String
. For each possible associated type, there's a distinct SpecialController
, and by extension, a distinct T
.
正如编译器所建议的那样,可以使用SpecialController的唯一方法是作为通用约束。你可以拥有一个比T更通用的函数,其约束条件是T必须是SpecialController。 T的域现在跨越所有各种具体类型的SpecialController,例如一个具有Int关联类型,一个具有String。对于每种可能的关联类型,都有一个独特的SpecialController,并且通过扩展,它是一个独特的T.
To draw out the Optional<T>
analogy further. Imagine if what you're trying to do was possible. It would be much like this:
进一步绘制Optional
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.
#1
3
Unfortunately, Swift doesn't currently support the use of protocols with associated types as actual types. This however is technically possible for the compiler to do; and it may well be implemented in a future version of the language.
不幸的是,Swift目前不支持使用具有相关类型的协议作为实际类型。然而,编译器在技术上可以这样做;它可能会在未来的语言版本中实现。
A simple solution in your case is to define a 'shadow protocol' that SpecialController
derives from, and allows you to access currentValue
through a protocol requirement that type erases it:
在您的情况下,一个简单的解决方案是定义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!")
}
#2
1
[Edited to fix: : SpecialValue
, not = SpecialValue
]
[编辑修复:: SpecialValue,not = SpecialValue]
This is not possible. SpecialValueController
is an "incomplete type" conceptually so the compiler cannot know. SpecialValueType
, although it is constrained by SpecialValue
, it is not known until it is determined by any adopting class. So it is a really placeholder with inadequate information. as?
-ness cannot be checked.
这不可能。 SpecialValueController在概念上是一个“不完整类型”,因此编译器无法知道。 SpecialValueType虽然受到SpecialValue的约束,但在任何采用类确定之前都不知道。所以它是一个真正的占位符,信息不足。因为? - 无法检查。
You could have a base class that adopts SpecialController
with a concrete type for SpecialValueController
, and have multiple child classes that inherit from the adopting class, if you're still seeking a degree of polymorphism.
你可以有一个基类,它采用SpecialController和SpecialValueController的具体类型,并且有多个继承自采用类的子类,如果你仍然在寻求一定程度的多态性。
#3
0
This doesn't work because SpecialController
isn't a single type. You can think of associated types as a kind of generics. A SpecialController
with its SpecialValueType
being an Int
is a completely different type from a SpecialController
with its SpecialValueType
being an String
, just like how Optional<Int>
is a completely different type from Optional<String>
.
这不起作用,因为SpecialController不是单一类型。您可以将关联类型视为一种泛型。 SpecialValueType为Int的SpecialController是一个与SpecialController完全不同的类型,其SpecialValueType是一个String,就像Optional
Because of this, it doesn't make any sense to cast to SpecialValueType
, because that would gloss over the associated type, and allow you to use (for example) a SpecialController
with its SpecialValueType
being an Int
where a SpecialController
with its SpecialValueType
being a String
is expected.
因此,转换为SpecialValueType没有任何意义,因为这会掩盖相关类型,并允许您使用(例如)SpecialController,其SpecialValueType为Int,其中SpecialController的SpecialValueType为字符串是预期的。
As compiler suggests, the only way SpecialController
can be used is as a generic constraint. You can have a function that's generic over T
, with the constraint that T
must be a SpecialController
. The domain of T
now spans all the various concrete types of SpecialController
, such as one with an Int
associated type, and one with a String
. For each possible associated type, there's a distinct SpecialController
, and by extension, a distinct T
.
正如编译器所建议的那样,可以使用SpecialController的唯一方法是作为通用约束。你可以拥有一个比T更通用的函数,其约束条件是T必须是SpecialController。 T的域现在跨越所有各种具体类型的SpecialController,例如一个具有Int关联类型,一个具有String。对于每种可能的关联类型,都有一个独特的SpecialController,并且通过扩展,它是一个独特的T.
To draw out the Optional<T>
analogy further. Imagine if what you're trying to do was possible. It would be much like this:
进一步绘制Optional
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.