[英]Swift enum associated value with different types
我有一個像這樣的 Swift 枚舉:
public enum AnimationType {
case position(Float)
case position([Keyframe<Float>])
case scale(Float)
case scale([Keyframe<Float>])
case rect(CGRect)
case rect([Keyframe<CGRect>])
case transform(CGAffineTransform)
case transform([Keyframe<CGAffineTransform>])
...
...
}
正如我們所見,對於每種類型,都有兩個可能的值 - 類型 T 的固定值或具有值類型 T ([Keyframe]) 的關鍵幀數組。 我想知道是否有什么辦法可以避免在枚舉中重復相同的名稱並合並兩個枚舉大小寫類型? 或者我以錯誤的方式建模?
對於每種變體,我會用Kind
枚舉類型解決這個問題。
public enum AnimationType {
public enum Kind<Value> {
case scalar(Value)
case keyframes([Keyframe<Value>])
}
case position(Kind<Float>)
case scale(Kind<Float>)
case rect(Kind<CGRect>)
case transform(Kind<CGAffineTransform>)
}
用法:
let anim1 = AnimationType.position(.scalar(10))
let anim2 = AnimationType.position(.keyframes([Keyframe(10)]))
獲取值:
switch anim1 {
case .position(let kind):
switch kind {
case .scalar(let value):
print("value: \(value)")
case .keyframes(let keyframes):
print("keyframes: \(keyframes)")
}
default: // You would implement the rest
break
}
switch anim1 {
case .position(.scalar(let value)):
print("value: \(value)")
case .position(.keyframes(let keyframes)):
print("keyframes: \(keyframes)")
default: // You would implement the rest
break
}
if case .position(.scalar(let value)) = anim1 {
print("value: \(value)")
}
您還可以添加Codable
一致性:
public struct Keyframe<Value: Codable> {
let value: Value
init(_ value: Value) {
self.value = value
}
}
extension Keyframe: Codable {
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(value)
}
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
value = try container.decode(Value.self)
}
}
public enum AnimationType {
public enum Kind<Value: Codable> {
case scalar(Value)
case keyframes([Keyframe<Value>])
}
case position(Kind<Float>)
case scale(Kind<Float>)
case rect(Kind<CGRect>)
case transform(Kind<CGAffineTransform>)
}
extension AnimationType.Kind: Codable {
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .scalar(let value): try container.encode(value)
case .keyframes(let keyframes): try container.encode(keyframes)
}
}
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let scalar = try? container.decode(Value.self) {
self = .scalar(scalar)
return
}
if let keyframes = try? container.decode([Keyframe<Value>].self) {
self = .keyframes(keyframes)
return
}
// You should throw error here instead
fatalError("Failed to decode")
}
}
extension AnimationType: Codable {
private enum CodingKeys: CodingKey {
case position
case scale
case rect
case transform
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
switch self {
case .position(let kind): try container.encode(kind, forKey: .position)
case .scale(let kind): try container.encode(kind, forKey: .scale)
case .rect(let kind): try container.encode(kind, forKey: .rect)
case .transform(let kind): try container.encode(kind, forKey: .transform)
}
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if let position = try? container.decode(Kind<Float>.self, forKey: .position) {
self = .position(position)
return
}
if let scale = try? container.decode(Kind<Float>.self, forKey: .scale) {
self = .scale(scale)
return
}
if let rect = try? container.decode(Kind<CGRect>.self, forKey: .rect) {
self = .rect(rect)
return
}
if let transform = try? container.decode(Kind<CGAffineTransform>.self, forKey: .transform) {
self = .transform(transform)
return
}
// You should throw error here instead
fatalError("Failed to decode")
}
}
示例編碼:
do {
let data = try JSONEncoder().encode(anim1)
if let str = String(data: data, encoding: .utf8) {
print(str)
// Prints: {"position":10}
}
} catch {
print(error)
}
與anim2
相同的事情返回{"position":[10]}
。
@George 的回答確實解決了這個問題。
Swift 的解決方案多種多樣。
這是我的建議:
public struct Keyframe<T> {
let v : T
}
public enum AnimaKind{
case simple
case series
}
public enum AnimatationType {
case position
case scale
case rect
case transform
}
extension AnimatationType{
func simple<T>(info: T) -> (type: AnimatationType, kind: AnimaKind, info: T){
return (self, .simple, info)
}
func series<T>(info: [T]) -> (type: AnimatationType, kind: AnimaKind, info: [Keyframe<T>]){
let result = info.map { x in Keyframe(v: x) }
return (self, .series, result)
}
}
如您所見,打開包裝的方式更容易
func test(){
let animaTest = AnimatationType.position.simple(info: Float(10))
}
你從animaTest
獲得價值很方便,
tuple
與enum nested
我假設在低端您使用一些通用代碼來利用這兩種類型,因此您可以通過將它們分組到某種協議下來使多態受益:
public enum AnimatationType {
case position(PositionProtocol)
case scale(ScaleProtocol)
case rect(RectProtocol)
case transform(TransformProtocol)
...
...
}
並簡單地擴展類型:
extension Float: PositionProtocol {
func someCommonGround() -> SomeCommonType { ... }
}
extension Keyframe: PositionProtocol where KeyframeGenericArgument == Float {
func someCommonGround() -> SomeCommonType { ... }
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.