【问题标题】:Specify a generic value argument as a parameter of an object initialization call将泛型值参数指定为对象初始化调用的参数
【发布时间】:2019-01-22 13:57:04
【问题描述】:

我有一个结构 Event,它使用时间和值进行初始化。

value 属性的类型在创建事件时决定。它可以是IntDouble 原语之一。

我应该如何在 Swift 中实现这一点?

我希望能够像这样创建一个新的Event 对象:

let event = Event(time: Date.init(), value: EventValue<Double>(40.3467)) 

我找到了this,但我找不到。

我已经尝试了很多这样的排列,我能做的最好的就是

struct Event {
  let time: Date
  var value: EventValue? // This line 'requires arguments in <...>'
}

struct EventValue <T> {
  let value: T?
}

【问题讨论】:

  • 我想知道(非通用)enum EventValue { case ival(Int); case dval(Double) } 是否是您真正想要的......
  • @JoshCaswell 感谢您的编辑。这也为我解决了问题;)
  • @MartinR 我也想知道我在寻找什么。感谢您的意见。

标签: swift generics


【解决方案1】:

也将泛型参数添加到您的Event 结构中,然后将此类型用于EventValue 的参数

struct Event<T> {
    let time: Date
    var value: EventValue<T>?
}

然后只需初始化EventValue 而不指定类型,因为编译器允许您仅传递与通用参数约束相对应的值。并且由于你的参数没有约束,它等于Any,所以你可以传递任何类型

let event = Event(time: Date.init(), value: EventValue(value: 40.3467))

【讨论】:

    【解决方案2】:

    由于EventValue 是通用的,它不能直接用作属性的类型。* 您必须直接指定类型参数,这将为您提供一个始终具有特定种类EventValue 的容器:

    struct DoubleEvent {    // Please pick a better name, though
        let time: Date
        var value: EventValue<Double>?
    }
    

    或者使容器也通用:

    struct Event<T> {
        let time: Date
        var value: EventValue<T>?
    }
    

    在任何一种情况下,您都不必在创建 Event 时显式指定类型:let event = Event(time: Date(), value: EventValue(value: 40.3467)) 类型推断将在 Event 本身是泛型时填充它。

    (旁白:请注意,T 没有正式限制只能是 DoubleInt。例如,let event = Event(time: Date(), value: EventValue(value: "123abc")) 也是可能的。如果您确实想严格避免这种情况,@987654321 @.)


    *不用说太多细节,它更像是“创建类型的东西”,而不是类型本身。

    【讨论】:

      【解决方案3】:

      如果打算有一个(单一的)Event 类型和一个EventValue 属性,它可以保存一个整数一个双精度值,那么enum 与相关值将用于此目的:

      enum EventValue {
          case ival(Int)
          case dval(Double)
      }
      
      struct Event {
          let time: Date
          let value: EventValue
      }
      
      let event1 = Event(time: Date(), value: .dval(40.3467))
      let event2 = Event(time: Date(), value: .ival(1234))
      

      【讨论】:

      • 这是我现在设置的方式。我认为这对于 Swift 的新手来说是一个很好的建议。我迷失了一些我不一定要理解的语言特性(即编译器标志无处不在)的细节。很高兴能继续前进。
      【解决方案4】:

      如果你想在同一个Event类型下支持各种EventValue(即Event没有泛型),你可以为EventValue存储一个协议引用:

      protocol EventValueProtocol {
          var time: Date { get }
          var value: SomeCommonGroundProtocolOrClass
      }
      
      struct Event {
          let time: Date
      
          // note that the generics information is lost
          // when using the protocol
          var value: EventValueProtocol
      
          init<T>(time: Date, value: EventValue<T>) {
              self.time = time
              self.value = value
          }
      }
      
      struct EventValue <T>: EventValueProtocol {
          let value: T?
      }
      

      “类型擦除器”协议的缺点是您丢失了用于EventValue 的通用参数的类型信息,但是您可能可以通过协议中的一些辅助方法来解决这个问题.例如,您可以为所有 T 泛型参数使用一个公分母,以便访问通用功能(除非您想切换 T 可以采用的所有可能类型,否则您有点需要这个)。

      【讨论】:

      • 我很好奇你会在协议中写什么类型的助手?如果您愿意分享它可能会帮助我更好地理解您的建议。
      猜你喜欢
      • 2019-03-16
      • 1970-01-01
      • 2015-03-22
      • 1970-01-01
      • 1970-01-01
      • 2010-12-07
      • 1970-01-01
      • 1970-01-01
      • 2012-01-07
      相关资源
      最近更新 更多