【问题标题】:Swift Function Input Generic Conform To Both Class And ProtocolSwift 函数输入通用符合类和协议
【发布时间】:2020-11-20 22:50:49
【问题描述】:

我试图让一个函数采用符合类和协议的通用输入,以便处理一些属于类而另一些属于委托的值。它适用于单个类型,但不适用于类型数组(同时符合超类和委托)。相反,编译器会抛出错误“实例方法'printValues(for:)'要求'MeasurementClass'符合'UnitDelegate'”

我添加了下面的代码,这将更有意义。只需将其放在 Swift Playground 中即可。如果您按原样运行它,您可以看到它正确地分别打印了用户的距离和步数。如果取消最后几行的注释,则不能将相同的函数用于数组。

我的目标是能够传递符合 UnitDelegate 和 MeasurementClass(又名 MeasurementObject 类型别名)的类型数组并处理值。我想要一个数组,因为我需要拥有一堆符合 MeasurementObject 的不同类。

protocol UnitDelegate: class{
    var units: [String: String] { get }
}

class MeasurementClass{
    var imperial: Double!
    var metric: Double!
    
    convenience init(imperial: Double, metric: Double){
        self.init()
        self.imperial = imperial
        self.metric = metric
    }
}

typealias MeasurementObject = MeasurementClass & UnitDelegate


class Distance: MeasurementObject{
    var units = ["imperial": "miles", "metric":"kms"]
}

class Steps: MeasurementObject{
    var units = ["imperial": "steps", "metric":"steps"]
}

class User{
    func printValues<T: MeasurementObject>(for type: T) {
        print("\(type.imperial!) \(type.units["imperial"]!) = \(type.metric!) \(type.units["metric"]!)")
    }
}

//This is what I'm trying to achieve in the for loop below
let distance = Distance(imperial: 30, metric: 48.28)
let steps    = Steps(imperial: 30, metric: 30)
let user     = User()
user.printValues(for: distance)
user.printValues(for: steps)


//let types = [distance, steps]
//
//for type in types{
//    user.printValues(for: type)
//}

【问题讨论】:

  • 这不是泛型的工作方式。您不能在集合中混合泛型类型。泛型类型可以是任何类型,但一次只能有一个
  • ` var Imperial:双倍! var metric: Double!` 我向你保证这两个值会不同步。仅存储一个事实来源,例如标准公制值,并使英制测量成为计算属性,可以即时得出正确的转换。这样,它就永远不会不同步
  • @Alexander-ReinstateMonica 我考虑了这一点并决定这是最好的。我在服务器上存储了一堆健康类型,通过存储英制和公制,我可以提取正确的值。如果我只有英制,我将不得不以某种方式打开被下拉的数据类型以进行转换。通过将所有健康记录以相同的值存储在服务器上,在本机应用程序端,我可以使用单个类来解码每种类型。
  • @RichardWitherspoon 我建议您将一个值作为唯一值得存储的值。查看 Foundation 中内置的 Units and Measurement API Apple。从我对您的代码/问题的了解来看,它看起来完全适合您正在尝试做的事情。如果没有,您至少可以参考它,例如它们的基本单位:developer.apple.com/documentation/foundation/dimension
  • 我不知道这个,这将是完美的!非常感谢。

标签: swift generics delegates protocols


【解决方案1】:

不是直接回答您的问题,但您只需要添加一个枚举以将测量类型存储到您的类中,并添加一个计算属性以返回相应的字典。无需使用协议、协议组合、类和/或子类来完成您想要做的事情:

struct AMeasurement {
    let imperial: Double
    let metric: Double
    let kind: Kind
    enum Kind { case distance, steps }
    var units: [String: String] {
        switch kind {
        case .distance: return ["imperial": "miles", "metric":"kms"]
        case .steps: return ["imperial": "steps", "metric":"steps"]
        }
    }
}

extension AMeasurement {
    func printValues() {
        print("\(imperial) \(units["imperial"]!) = \(metric) \(units["metric"]!)")
    }
}

let distance = AMeasurement(imperial: 30, metric: 48.28, kind: .distance)
let steps = AMeasurement(imperial: 30, metric: 30, kind: .steps)
let types = [distance, steps]

for type in types {
    type.printValues()
}

30.0 英里 = 48.28 公里
30.0 步 = 30.0 步

【讨论】:

  • 这是解决我的问题的一种更简单的方法。我似乎把答案复杂化了。感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-08-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多