繁体   English   中英

如何在 Swift 通用类型上专门化协议方法?

[英]How to specialize protocol method on Swift Generic Type?

struct Point<T> {
    var x: T
    var y: T
}

extension Point: CustomStringConvertible  {
    var description: String {
        return "\(x), \(y)"
    }
}

// error: Conflicting conformance of 'Point<T>' to protocol 'CustomStringConvertible'; there cannot be more than one conformance, even with different conditional bounds
extension Point: CustomStringConvertible where T == Double{
    var description: String {
        return String(format: "%.3f, %.3f", x, y)
    }
}
print(Point(x: 1, y: 2)) // output: 1, 2
print(Point(x: Double(1.111111), y: Double(2.222222))) // expected output: 1.111, 2.222

如何专门化 CustomStringConvertible.description: Double类型的CustomStringConvertible.description: String方法?

编译错误显示我不能两次扩展相同的协议。

试验 1:如果我删除了扩展语法: CustomStringConvertible ,它会编译。 但是Point<Double>.description在调用print(Point<Double>(...))时不会被调用。

Trail 2:如果我在第一个扩展名处添加where T == Int ,则会显示相同的编译错误。

在 Swift 中专门扩展方法的正确方法是什么?

CustomStringConvertible是一个协议,它需要一个符合要求的类/结构来采用一个名为description的属性。

编译器向您显示此错误,因为您只有一个泛型类型 struct Point尝试两次创建/实现此协议的此必需属性。 你不能那样做。 您的结构符合CustomStringConvertible并且只需要实现 1 个description属性。

所以在你的情况下,我会做以下事情:

在此处输入图像描述

description属性返回某些内容之前,它可以进行基本的 if-else 检查。 让我知道这是否有帮助..!

Swift 没有与 C++ 相同的专业化概念。 在此处查看我最近的答案,该答案解释了与 C++ 模板编程的一些差异。

另一方面,协议特化只是静态分派的,所以即使你的代码被允许,它们也不会按照你想要的方式工作。 在此处查看此答案。

Swift 依赖于一种更传统的面向对象的方法,该方法的特点是通过协议(这在 C++ 中不常见)明确说明合同。

您希望 T 根据您的规则可转换为字符串。 所以为此定义一个协议:

protocol PointStringConvertible {
    var pointString: String { get }
}

struct Point<T: PointStringConvertible> {
    var x: T
    var y: T
}

现在为 Double 实现 PointStringConvertible:

extension Double: PointStringConvertible {
    var pointString: String {
        String(format: "%.3f", self)
    }
}

在 Point 中使用您的新协议:

extension Point: CustomStringConvertible  {
    var description: String {
        "\(x.pointString), \(y.pointString)"
    }
}

它会起作用:

print(Point(x: 12, y: 43))

打印12.000, 43.000

但是,这不适用于不符合您的协议的类型:

print(Point(x: true, y: false))

会出错。 在我看来,这很有意义。

您可以扩展所有符合 CustomStringConvertible 的类型,以便为其他类型轻松实现 PointStringConvertible:

extension PointStringConvertible where Self: CustomStringConvertible {
    var pointString: String {
        description
    }
}

extension Bool: PointStringConvertible {}

但是当然,这仍然需要声明一致性,这不是一件坏事。 如果你不喜欢这样,你仍然可以这样做:

struct Point<T> {
    var x: T
    var y: T
}

extension Double: PointStringConvertible {
    var pointString: String {
        String(format: "A double %.3f", self)
    }
}

extension Point: CustomStringConvertible  {
    private func pointString(for val: T) -> String {
        (val as? PointStringConvertible)?.pointString ?? "\(val)"
    }

    var description: String {
        "\(pointString(for: x)), \(pointString(for: y))"
    }
}

print(Point(x: 12.0, y: 43.0))
print(Point(x: true, y: false))

给予:

A double 12.000, A double 43.000
true, false

这是否是好的设计是另一个问题。 请注意,您当然也可以简单地切换 T 的类型:

struct Point<T> {
    var x: T
    var y: T
}

extension Point: CustomStringConvertible  {
    private func pointString(for val: T) -> String {
        switch val {
        case let double as Double:
            return String(format: "A double %.3f", double)
        default:
            return "\(val)"
        }
    }

    var description: String {
        "\(pointString(for: x)), \(pointString(for: y))"
    }
}

print(Point(x: 12.0, y: 43.0))
print(Point(x: true, y: false))

但这可能构成了对 Double in Point 的了解。

暂无
暂无

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

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