【问题标题】:How do I return an unspecified Measurement<T> from a function?如何从函数返回未指定的 Measurement<T>?
【发布时间】:2020-09-08 17:38:07
【问题描述】:

更新:好的,我很困惑。我的原始代码没有编译。在将其缩减为最小示例的某个时候,它显然开始工作了。我已经重写了原始版本,因为我已经忘记了为什么它一开始就不起作用。只想将此问题标记为已解决。


我试图弄清楚如何编写一个返回任意测量值的函数,而不必事先指定测量值具有什么 UnitType。以下或多或少是我想做的,但它没有编译。 (函数的参数无关紧要。)

   func getValue(for x:Int) -> Measurement<Unit>? {
        if x==1 {
            return Measurement(value:5, unit:UnitSpeed.metersPerSecond)
        } else {
            return Measurement(value:2, unit:UnitLength.meters)
        }
    }

我不想关心单位是什么!测量值封装了它们的单元,我要做的就是将其插入到另一端,例如:Text(reading?.unit.symbol ?? "")

func getValue(for measure:TrackedMeasure) -&gt; some Measurement? {} 抱怨“引用泛型类型 'Measurement' 需要 <...> 中的参数”

建议的修复方法是 func getValue(for measure:TrackedMeasure) -&gt; some Measurement&lt;Unit&gt;? { },然后它会抱怨“‘不透明’类型必须仅指定‘Any’、‘AnyObject’、协议和/或基类”

似乎没有一种测量协议,我不知道如何定义。

我是否必须重新实现 Measurement 才能完成这项工作?

【问题讨论】:

  • 泛型是静态类型的东西。它们不会像协议那样擦除类型。谈论没有单位的测量只是“双重”。您试图用这段代码解决的根本问题是什么?
  • 什么是TrackedMeasure?无法真正理解您要在这里完成的工作。你有一个例子说明你想要传递什么并得到回报吗?
  • TrackedMeasure 只是一个枚举。我想传入一个值,选择一个测量值并返回它。但测量值并不完全相同:我有一个速度、一个距离和一个经过的时间。
  • 考虑一个特殊类型的例子。在您的示例中,您希望函数返回 Measurement&lt;UnitLength&gt; 类型或 Measurement&lt;UnitSpeed&gt; 类型。这些是完全不同的类型,没有共享的公共基础。你可以返回Any,但这可能不是你想要的。
  • 您要完成的工作仍然模棱两可。更多上下文肯定会有所帮助,因为它似乎可能是 XY problem

标签: swift generics opaque-types


【解决方案1】:

Measurement&lt;UnitType&gt; 是泛型类型,它不扩展公共基类或遵循公共协议,因此具体类型 Measurement&lt;UnitLength&gt;Measurement&lt;UnitSpeed&gt; 是不同的类型。

解决它的唯一方法是执行类型擦除之类的操作,并让它们都符合通用协议AnyMeasurement

protocol AnyMeasurement {
  var baseUnit: Unit { get }
  var value: Double { get }
}

那么你就可以将Measurement 符合AnyMeasurement:

extension Measurement: AnyMeasurement {
  // just need to return a base Unit, instead of a concrete UnitType
  // value: Double { get } is already implemented by Measurement
  var baseUnit: { self.unit }
}

然后,你可以这样做:

func randomMeasurement() -> AnyMeasurement {
   let speed  = Measurement(value: 10, unit: UnitSpeed.metersPerSecond)
   let length = Measurement(value: 10, unit: UnitLength.meters)

   return Bool.random() ? speed : length
}

print(randomMeasurement().baseUnit.symbol)

【讨论】:

  • 好的,谢谢。所以通用协议有\.value和\.baseUnit,而不是Measurement的\.value和\.unit。它感觉更像是一个包装器,而不是一个通用的基类,但如果这是我们能做到的最好的,它就会发挥作用。
  • 嗯,它是一个扩展,而不是一个包装器。它使Measurement&lt;T&gt; 的所有具体类型都符合新协议。例如,\.value 仍然是 Measurement 自己的属性
【解决方案2】:
enum TrackMeasure {
    case speed
    case distance
    case time
}

func getValue(for measure: TrackMeasure) -> Measurement<Unit> {

    switch measure {
        case .speed:
            return Measurement(value: 60, unit: UnitSpeed.milesPerHour)
        case .distance:
            return Measurement(value: 10, unit: UnitLength.miles)
        case .time:
            return Measurement(value: 6, unit: UnitDuration.minutes)
    }
}

let speed = getValue(for: .speed)
let distance = getValue(for: .distance)
let time = getValue(for: .time)

【讨论】:

  • 恐怕我不想采取通用测量;我想返回一个通用的测量值。
猜你喜欢
  • 1970-01-01
  • 2019-06-08
  • 1970-01-01
  • 1970-01-01
  • 2015-03-23
  • 2012-01-15
  • 1970-01-01
  • 1970-01-01
  • 2021-02-14
相关资源
最近更新 更多