在Swift中,如何转换为具有关联类型的协议?

时间:2022-09-11 19:13:34

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 是一个与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 是一个与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.