在实现Equatable的结构体上进行操作 [英] Operation on an array of structs implementing Equatable

查看:217
本文介绍了在实现Equatable的结构体上进行操作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一系列不同的结构,都实现了Equatable协议,并试图将其传递给需要集合where T.Iterator.Element: Equatable的函数.我知道如何通过使用类并仅创建class Vehicle: Identifiable, Equatable来解决此问题,然后使CarTractor实现Vehicle.但是我想知道使用结构和协议是否可行?

I have an array of different structs, all implementing Equatable protocol and am trying to pass it to a function that expects a collection where T.Iterator.Element: Equatable. I know how to solve this problem by using classes and just creating a class Vehicle: Identifiable, Equatable, and then make Car and Tractor implement Vehicle. However I'd like to know if this is possible with using structs and protocols?

这是我想要做的一个人为的例子

Here's a contrived example of what I'm trying to do

//: Playground - noun: a place where people can play

protocol Identifiable {
    var ID: String { get set }
    init(ID: String)
    init()
}

extension Identifiable {
    init(ID: String) {
        self.init()
        self.ID = ID
    }
}

typealias Vehicle = Identifiable & Equatable

struct Car: Vehicle {
    var ID: String

    init() {
        ID = ""
    }

    public static func ==(lhs: Car, rhs: Car) -> Bool {
        return lhs.ID == rhs.ID
    }
}

struct Tractor: Vehicle {
    var ID: String

    init() {
        ID = ""
    }

    public static func ==(lhs: Tractor, rhs: Tractor) -> Bool {
        return lhs.ID == rhs.ID
    }
}

class Operator {
    func operationOnCollectionOfEquatables<T: Collection>(array: T) where T.Iterator.Element: Equatable {
    }
}

var array = [Vehicle]() //Protocol 'Equatable' can only be used as a generic constraint because Self or associated type requirements

array.append(Car(ID:"VW"))
array.append(Car(ID:"Porsche"))
array.append(Tractor(ID:"John Deere"))
array.append(Tractor(ID:"Steyr"))

var op = Operator()
op.operationOnCollectionOfEquatables(array: array) //Generic parameter 'T' could not be inferred

推荐答案

问题是,如错误所示,您不能使用具有Self或相关类型要求的协议作为实际类型-因为您会丢失有关什么的类型信息这些要求是.在这种情况下,您将丢失==实现的参数的类型信息–因为Equatable表示它们必须与一致类型(即Self)相同.

The problem is, as the error says, you cannot use protocols with Self or associated type requirements as actual types – as you'd lose the type information for what those requirements were. In this case, you'd lose the type information for the parameters of the == implementation – as Equatable says they must be the same type as the conforming type (i.e Self).

解决方案几乎总是建立一个类型的橡皮擦.在期望类型相等的情况下,如果它们的id属性相等,则可以像存储id属性并将其在==实现中进行比较一样简单.

The solution is almost always to build a type eraser. In the case of expecting types to be equal if their id properties are equal, this can be as simple as just storing the id property and comparing it in the == implementation.

struct AnyVehicle : Equatable {

    static func ==(lhs: AnyVehicle, rhs: AnyVehicle) -> Bool {
        return lhs.id == rhs.id
    }

    let id : String

    init<T : Vehicle>(_ base: T) {
        id = base.id
    }
}

(请注意,为了符合Swift命名约定,我将您的ID属性重命名为id)

(Note that I renamed your ID property to id in order to conform with Swift naming convention)

但是,更通用的解决方案是在类型橡皮擦中存储一个函数,该函数可以在类型转换之后基于 ==实现比较两个任意符合Vehicle的实例它们与创建类型橡皮擦的具体类型相同.

However, a more general solution would be to store a function in the type eraser that can compare two arbitrary Vehicle conforming instances based on their == implementation, after type-casting to ensure they are the same type as the concrete type that the type eraser was created with.

struct AnyVehicle : Equatable {

    static func ==(lhs: AnyVehicle, rhs: AnyVehicle) -> Bool {

        // forward to both lhs's and rhs's _isEqual in order to determine equality.
        // the reason that both must be called is to preserve symmetry for when a
        // superclass is being compared with a subclass.
        // if you know you're always working with value types, you can omit one of them.
        return lhs._isEqual(rhs) || rhs._isEqual(lhs)
    }

    let base: Identifiable

    private let _isEqual: (_ to: AnyVehicle) -> Bool

    init<T : Vehicle>(_ base: T) {

        self.base = base

        _isEqual = {

            // attempt to cast the passed instance to the concrete type that
            // AnyVehicle was initialised with, returning the result of that
            // type's == implementation, or false otherwise.
            if let other = $0.base as? T {
                return base == other
            } else {
                return false
            }
        }
    }
}

print(AnyVehicle(Car(id: "foo")) == AnyVehicle(Tractor(id: "foo"))) // false
print(AnyVehicle(Car(id: "foo")) == AnyVehicle(Car(id: "bar"))) // false
print(AnyVehicle(Car(id: "foo")) == AnyVehicle(Car(id: "foo"))) // true

var array = [AnyVehicle]()

array.append(AnyVehicle(Car(id: "VW")))
array.append(AnyVehicle(Car(id: "Porsche")))
array.append(AnyVehicle(Tractor(id: "John Deere")))
array.append(AnyVehicle(Tractor(id: "Steyr")))

var op = Operator()

// compiles fine as AnyVehicle conforms to Equatable.
op.operationOnCollectionOfEquatables(array: array) 

这篇关于在实现Equatable的结构体上进行操作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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