扩展类型以具有相同关联类型的多个值 [英] Extending a type to have multiple values for the same associatedtype

查看:58
本文介绍了扩展类型以具有相同关联类型的多个值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

想出一个准确的标题有点困难,所以我会用一个简单的例子来说明我的问题.我想创建一个协议,指定一个遵守者能够被转换成不同的类型:

Coming up with an accurate title was a little difficult so I'll demonstrate my issue with a simple example. I want to create a protocol that specifies that an adherer is able to be transformed into a different type:

protocol Transformable {
    associatedtype TransformableTo
    func transform() -> TransformableTo
}

然后为每种类型创建特定的协议:

Then create specific protocols for each type:

protocol DoubleTransformable: Transformable where TransformableTo == Double {}

例如,如果我希望字符串能够转换为双精度数,我会这样写:

So that if, for example, I want Strings to be able to be converted into Doubles, I would write:

extension String: DoubleTransformable {
    func transform() -> Double {
        return Double(self)
    }
}

这很好用,直到我想让给定的类型符合更多这些特定"可转换合约:

This works great until I want to have a given type conform to more than of these "specific" transformable contracts:

protocol DecimalTransformable: Transformable where TransformableTo == Decimal {}
extension String: DecimalTransformable {
    func transform() -> Decimal {
        ...
    }
}

由于DecimalTransformableTransformableTo 类型与DoubleTransformable 的TransformableTo 类型发生冲突,因此编译器对上面的字符串扩展向我咆哮.这对我来说很有意义,但我没有看到解决方法.任何关于如何解决这个问题和/或完成相同事情的不同方向的建议都将不胜感激.

The compiler barks at me on the String extension above since the TransformableTo type for DecimalTransformable clashes with the TransformableTo type for DoubleTransformable. This makes sense to me but I'm not seeing a workaround. Any advice on how to solve this and/or a different direction that accomplishes the same thing would be appreciated.

编辑

添加更多关于用例的上下文,以帮助解决评论中的一些问题.这是实际代码的简化版本,所以这可能看起来有点奇怪,但我认为它明白了.

Adding some more context about the use case to help address some of the questions in the comments. This is a simplified version of the actual code so this might seem a little strange, but I think it gets the point across.

我有两个结构体,一个表示面向公众的模型,一个表示支持"模型的数据.该模型.我不想将 PrivateModel 中的数据公开给客户端,因此 PublicModel 充当 PrivateModel 和客户端之间的中间人.我试图解决的问题是,有时 PrivateModel 属性的类型与我希望 PublicModel 的相应属性不同(并且更改 PrivateModel 的类型是不可能的).

I have two structs, one represents a public-facing model and one represents data that "backs" the model. I don't want to expose the data in the PrivateModel to clients so the PublicModel acts as middleman between PrivateModel and the client. The issue I'm trying to solve is that sometimes the type of a PrivateModel property is different than what I want the PublicModel's corresponding property to be (and changing the type of PrivateModel is a non-starter).

struct PrivateModel {
    var date: String { return "Jan. 1, 1970" }
}

struct PublicModel {
    private let privateModel: PrivateModel

    init(source: PrivateModel) {
        self.privateModel = source
    }

    var date: Date {
        return self.privateModel.date.transform()
    }
}

注意公共模型中对 transform 的调用.我在这里的实际目标是拥有一个可以在任何类型对象上调用的单一转换方法,其中 transform 的通用返回类型是由我所在的 getter 的属性类型推断出来的.>

Note the call to transform within the public model. My actual goal here is to have a single transformation method that I can call on any type object, where the generic return type of transform is inferred by the type of property whose getter I'm inside.

推荐答案

不是对您问题的直接回答,但正如其他人已经提到的,关联类型不是通用的.你需要的是一个通用的方法.要完成您想要的并涵盖大多数数字类型,您还需要使泛型类型符合 LosslessStringConvertible 协议.请注意,协议的使用不是必需的,可以删除.我会做这样的事情:

Not a direct answer to your question but as others have already mentioned associated types are not generic. What you need is a generic method. To accomplish what you want and cover most numeric types you need to conform the generic type to LosslessStringConvertible protocol as well. Note that the use of a protocol is not necessary and can be removed. I would do something like this:

protocol NumericTransformable {
    func numeric<N: Numeric & LosslessStringConvertible>() -> N?
}


extension String: NumericTransformable {
    func numeric<N: Numeric & LosslessStringConvertible>() -> N?  { N(self) }
}


用法:


Usage:

let double: Double? = "123.45".numeric()       // 123.45
let float: Float? = "123.45".numeric()         // 123.45
let float80: Float80? = "123.45".numeric()     // 123.45
let int: Int? = "123".numeric()                // 123


还要注意 Decimal 和 CGFloat 类型不符合 LosslessStringConvertible:

extension CGFloat: LosslessStringConvertible {
    private static let formatter = NumberFormatter()
    public init?(_ description: String) {
        guard let cgFloat = Self.formatter.number(from: description) as? CGFloat else { return nil }
        self = cgFloat
    }
}


extension Decimal: LosslessStringConvertible {
    public init?(_ description: String) {
        guard let decimal = Decimal(string: description) else { return nil }
        self = decimal
    }
}


let cgfloat: CGFloat? = "123.45".numeric()     //  123.45
let decimal: Decimal? = "123.45".numeric()     //  123.45

这篇关于扩展类型以具有相同关联类型的多个值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆