swift 通用枚举关联的值扩展,可能吗?

mefy6pfw  于 2023-04-04  发布在  Swift
关注(0)|答案(2)|浏览(131)

我们有enum:

enum Line {
    case horizontal(CGFloat)
    case vertical(CGFloat)
    case diagonal(CGFloat, CGFloat)
}

默认情况下,我们可以使用3 ways to get value from ValueAssociatedEnum
但所有这些方法看起来都很可怕。
恕我直言。
问题是:
是否可以为enum创建通用扩展以使用以下类似语法:
样品1:

let lineH = Line.horizontal(10)

if let val = lineH.is(Line.horizontal) {
     print("lineHorizontalValue: \(val)")             // lineHorizontalValue: 10
}

样品二:

let lineD = diagonal(10, 20)

if let (point1, point2) = lineD.is(Line.diagonal) {
     print("lineDiagonalValue: \(point1), \(point2)") // lineDiagonalValue 10, 20
}
s3fp2yjn

s3fp2yjn1#

同一个函数不能有不同的返回类型,你需要的是:

enum Line {
    case horizontal(CGFloat)
    case vertical(CGFloat)
    case diagonal(CGFloat, CGFloat)
}

extension Line {
    var asDiagonalValues : (CGFloat, CGFloat)? {
        switch self {
        case .diagonal(let p1, let p2): return (p1,p2)
        default: return nil
        }
    }
    
    var asVertialValue : CGFloat? {
        switch self {
        case .vertical(let p1): return p1
        default: return nil
        }
    }
}

您可能还需要像这样的操作符:

func ~= ( left: Line, right: Line) -> Bool {
    switch (left,right) {
    case (.horizontal(_), .horizontal(_)): return true
    case (.vertical(_), .vertical(_)): return true
    case (.diagonal(_,_), .diagonal(_,_)): return true
    default: return false
    }
}

Line.horizontal(20) ~= Line.horizontal(30) // returns true

扩展如下:

extension Line {
    var asUno: CGFloat? {
        switch self {
        case .vertical(let p): return p
        case .horizontal(let p): return p
        default: return nil
        }
    }
    
    var asDuo: (CGFloat,CGFloat)? {
        switch self {
        case .diagonal(let p1, let p2): return (p1,p2)
        default: return nil
        }
    }
}

你可以这样写代码:

var line = Line.diagonal(1, 2)

if line ~= Line.diagonal(0,0), let vals = line.asDuo {
    let (p1,p2) = vals
    
    print(p1,p2)
}
2ul0zpep

2ul0zpep2#

我读过很多消息,说同一个函数不可能有不同的返回类型,但这是可能的,正如你在这里看到的:)
基于Jessy's code
注意事项:

  • 这段代码是错误的方法来处理枚举关联值的(!!!)。最好使用standard syntax
  • 这段代码比标准语法运行得慢。
  • 这段代码将不工作的情况下,你有几个枚举案件相同的名称,但不同的关联值类型。
  • 永远不要在生产中使用它。

这是无用的,但仍然,这是一个非常有趣的实验。非常感谢,杰西和扫地机。
使用示例:

enum Line: isAble {
     case horizontal(CGFloat)
     case vertical(CGFloat)
     case diagonal(CGFloat, CGFloat)
 }

 let lineD = Line.diagonal(10, 20)
 
 // will print "lineDiagonalValue: (10, 20)"
 if let (val1, val2) = lineD.is(Line.diagonal) {
      print("lineDiagonalValue: (\(val1), \(val2))") 
 }
 

 if let val = lineH.is(Line.horizontal) {
      print("this code will never work")
 }
import Foundation

/* usage sample
 enum Line: isAble {
     case horizontal(CGFloat)
     case vertical(CGFloat)
 }

 let lineH = Line.horizontal(10)
 
 // will print "lineHorizontalValue: 10"
 if let val = lineH.is(Line.horizontal) {
      print("lineHorizontalValue: \(val)") 
 }
 

 if let val = lineH.is(Line.horizontal) {
      print("this code will never work")
 }
*/

protocol isAble { }

extension isAble {
    public func `is`<T>( _ val: (T) -> isAble) -> T? {
        return Mirror.associatedValue(of: self, ifCase: val)
    }

    public func haveValue<T>( ofCase val: (T) -> isAble) -> Bool {
        return Mirror.associatedValue(of: self, ifCase: val) != nil
    }
}

/// Match `enum` cases with associated values, while disregarding the values themselves.
/// - Parameter case: Looks like `Enum.case`.
public func ~= <Enum: Equatable, AssociatedValue>(
    case: (AssociatedValue) -> Enum,
    instance: Enum
) -> Bool {
    Mirror.associatedValue(of: instance, ifCase: `case`) != nil
}

/// Match `enum` cases with associated values, while disregarding the values themselves.
/// - Parameter case: Looks like `Enum.case`.
public func ~= <Enum, AssociatedValue>(
    case: (AssociatedValue) -> Enum,
    instance: Enum
) -> Bool {
    Mirror.associatedValue(of: instance, ifCase: `case`) != nil
}

/// Match non-`Equatable` `enum` cases without associated values.
public func ~= <Enum>(pattern: Enum, instance: Enum) -> Bool {
    guard (
        [pattern, instance].allSatisfy {
            let mirror = Mirror(reflecting: $0)
            return mirror.displayStyle == .enum && mirror.children.isEmpty
        }
    ) else { return false }
    
    return .equate(pattern, to: instance) { "\($0)" }
}

public extension Mirror {
    /// Get an `enum` case's `associatedValue`.
    static func associatedValue<AssociatedValue>(
        of subject: Any,
        _: AssociatedValue.Type = AssociatedValue.self
    ) -> AssociatedValue? {
        guard let childValue = Self(reflecting: subject).children.first?.value
        else { return nil }
        
        if let associatedValue = childValue as? AssociatedValue {
            return associatedValue
        }
        
        let labeledAssociatedValue = Self(reflecting: childValue).children.first
        return labeledAssociatedValue?.value as? AssociatedValue
    }
    
    /// Get an `enum` case's `associatedValue`.
    /// - Parameter case: Looks like `Enum.case`.
    static func associatedValue<Enum: Equatable, AssociatedValue>(
        of instance: Enum,
        ifCase case: (AssociatedValue) throws -> Enum
    ) rethrows -> AssociatedValue? {
        try associatedValue(of: instance)
            .filter { try `case`($0) == instance }
    }
    
    /// Get an `enum` case's `associatedValue`.
    /// - Parameter case: Looks like `Enum.case`.
    static func associatedValue<Enum, AssociatedValue>(
        of instance: Enum,
        ifCase case: (AssociatedValue) throws -> Enum
    ) rethrows -> AssociatedValue? {
        try associatedValue(of: instance).filter {
            .equate(try `case`($0), to: instance) {
                Self(reflecting: $0).children.first?.label
            }
        }
    }
}

public extension Optional {
    /// Transform `.some` into `.none`, if a condition fails.
    /// - Parameters:
    ///   - isSome: The condition that will result in `nil`, when evaluated to `false`.
    func filter(_ isSome: (Wrapped) throws -> Bool) rethrows -> Self {
        try flatMap { try isSome($0) ? $0 : nil }
    }
}

public extension Equatable {
    /// Equate two values using a closure.
    static func equate<Wrapped, Equatable: Swift.Equatable>(
        _ optional0: Wrapped?, to optional1: Wrapped?,
        using transform: (Wrapped) throws -> Equatable
    ) rethrows -> Bool {
        try optional0.map(transform) == optional1.map(transform)
    }
}

相关问题