简体   繁体   English

Swift 泛型/不透明类型:尝试使用协议一致性在泛型函数中的类型之间进行转换

[英]Swift Generics/Opaque Types: trying to use protocol conformance to convert between types in a generic function

I'm building a Converter to convert my DataObject s to ModelObject s and vice versa.我正在构建一个Converter来将我的DataObject转换为ModelObject ,反之亦然。 I currently have two functions to do the converting.我目前有两个功能来进行转换。 One for each conversion.每次转换一个。

Currently, both of my conversion functions do exactly the same thing with exceptions to the type it takes and the type it returns.目前,我的两个转换函数都做同样的事情,除了它所采用的类型和它返回的类型。 I was hoping I could write a generic function that would be able to handle both conversions because, otherwise, the code is exactly the same.我希望我可以编写一个能够处理两种转换的通用函数,因为否则代码完全相同。

Here's what I've got: Obviously these are simplified examples这是我所得到的:显然这些是简化的示例

I've built a Protocol that contains the requirements for properties and an init() .我已经构建了一个包含属性要求和init()Protocol I've implemented this Protocol and its init() in both the DataObject and ModelObject .我已经在DataObjectModelObject中实现了这个Protocol及其init()

My Protocol :我的Protocol

protocol MyProtocol {
    var id: UUID { get }
    var name: String? { get }

    init(id: UUID, name: String?)
}

My DataObject :我的DataObject

class DataObject: Object, MyProtocol {
    @Persisted(primaryKey: true) var id: UUID

    @Persisted var name: String?

    required convenience init(id: UUID, name: String?) {
        self.init()
        self.id = id
        self.name = name
    }
}

My ModelObject :我的ModelObject

struct ModelObject: MyProtocol {
    var id: UUID
    var name: String?

    init(id: UUID, name: String?) {
        self.id = id
        self.name = name
    }
}

Currently: This is an example what is currently doing the conversions.当前:这是当前正在进行转换的示例。 I have one like this for each conversion.每次转换我都有一个这样的。

static func buildModelObject(object: DataObject) -> ModelObject {
    let returnObject = ModelObject(id: object.id, name: object.name)
    return returnObject
}

Desired Converter Function: I hope to accomplish something like this.所需的Converter功能:我希望完成这样的事情。

static func buildObject<O: MyProtocol, R: MyProtocol>(objectIn: O, returnType: R.Type) -> R {
    let returnObject = returnType.init(id: objectIn.id, name: objectIn.name)
    return returnObject
}

And I'm calling it like this:我这样称呼它:

let object = Converter.buildObject(objectIn: dataObject, returnType: ModelObject.self)

Obviously, this isn't working.显然,这是行不通的。 Error: "Protocol 'MyProtocol' as a type cannot conform to the protocol itself".错误: “协议 'MyProtocol' 作为一种类型不能符合协议本身”。 The error is on the function call.错误在函数调用上。 This is the only error XCode is showing me at compile.这是 XCode 在编译时向我显示的唯一错误。

I'm not certain what to do here or how to get it working.我不确定在这里做什么或如何让它工作。 I'm not even confident that this error message is even helpful in any way.我什至不相信此错误消息是否有任何帮助。

The Question:问题:

Is it even possible to accomplish something like this?甚至有可能完成这样的事情吗? If so, what am I missing?如果是这样,我错过了什么? I'm still very unfamiliar with Swift Generics.我对 Swift 泛型仍然很陌生。

I don't have an issue with using multiple conversion functions as it's still better than getting the entire code base bogged down with Realm specific stuff.我没有使用多个转换函数的问题,因为它仍然比让整个代码库陷入 Realm 特定的东西要好。 But, given that these functions are essentially the same, I'm hoping that Generics might be able to help reduce otherwise similar code into a single function.但是,鉴于这些函数本质上是相同的,我希望泛型能够帮助将其他类似的代码减少到一个函数中。

Note: The DataObject is a Realm Object .注意: DataObject是一个 Realm Object I understand they have cool features like live/managed Objects.我了解它们具有很酷的功能,例如实时/托管对象。 We're trying to keep our Persistence layer completely separated from the rest of the app with a single API/Entry Point.我们正在尝试使用单个 API/入口点将 Persistence 层与应用程序的其余部分完全分开。 This is why I'm using these Converters.这就是我使用这些转换器的原因。

You almost had it你几乎拥有它

func buildObject<R: MyProtocol>(objectIn: MyProtocol, returnType: R.Type) -> R {
    let returnObject = returnType.init(id: objectIn.id, name: objectIn.name)
    return returnObject
}

the key observation: at no point do we need the concrete type of objectIn , so just require the protocol type.关键观察:我们在任何时候都不需要objectIn的具体类型,所以只需要协议类型。

You don't need the other argument.你不需要另一个论点。 The return type already provides the type information.返回类型已经提供了类型信息。

func buildObject<R: MyProtocol>(objectIn: some MyProtocol) -> R {
  .init(id: objectIn.id, name: objectIn.name)
}

As suggested in the comments, this is more idiomatically expressed as an initializer.正如评论中所建议的,这更习惯性地表示为初始化程序。

extension MyProtocol {
  init(_ objectIn: some MyProtocol) {
    self.init(id: objectIn.id, name: objectIn.name)
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM