扩展Element类型为通用的Collection(Swift)

时间:2017-07-07 22:31:32

标签: swift generics

在swift中扩展Collection类型以使特定元素类型具有自定义函数非常简单:

struct MyStruct {
    let string: String
}

extension Set where Element == MyStruct {
    func getFirstWith(string: String) -> Element? {
        return filter({ $0.string == string }).first
    }
}

但是假设您的Element类型是通用的?

protocol MyProtocol {}

struct MyGenericStruct<T: MyProtocol> {
    let string: String
}

// error on next line:
// error: expected '>' to complete generic argument list
// extension Set where Element == MyGenericStruct<T: MyProtocol> {
//                                                 ^
extension Set where Element == MyGenericStruct<T: MyProtocol> {
    func getFirstWith(string: String) -> Element? {
        return filter({ $0.string == string }).first
    }
}

// error on next line:
// error: use of undeclared type 'T' 
// extension Set where Element == MyGenericStruct<T> {
//                                                ^
extension Set where Element == MyGenericStruct<T> {
    func getFirstWith(string: String) -> Element? {
        return filter({ $0.string == string }).first
    }
}

我不清楚如何声明我的元素是通用的。

3 个答案:

答案 0 :(得分:1)

如果我了解目的,您可能需要在protocolstructure中实施更多实施。

首先,元素的协议必须是可混合且公平的。

protocol MyProtocol: Hashable, Equatable {
    var string: String { get }
}

因此,MyGenericStruct将如下所示。

struct MyGenericStruct: MyProtocol {
    let string: String
    var hashValue: Int {
        get {
            return string.hashValue
        }
    }

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

然后,使用MyProtocol

的where子句指定的约束声明扩展名
extension Set where Element: MyProtocol {
    func getFirstWith(string: String) -> Element? {
        return filter({$0.string == string}).first
    }
}

最后,让我们做一个测试并查看其结果。

// Example
let set: Set = [
    MyGenericStruct(string: "Watermelon"),
    MyGenericStruct(string: "Orange"),
    MyGenericStruct(string: "Banana")
]
let output = set.getFirstWith(string: "Orange")
print(output)

在我的Xcode 8游乐场,我可以在日志中获得Optional(MyGenericStruct(string: "Orange"))

[UPDATED1]仅在ExtensionSet<MyGenericStruct>

extension Set where Element == MyGenericStruct {
    func getFirstWith(string: String) -> Element? {
        return filter({$0.string == string}).first
    }
}

[UPDATED2]如果需要保留MyGenericStruct<T: MyProtocol>声明,则实施Set扩展的另一种方法是:


protocol MyProtocol {

}

struct BaseStruct1: MyProtocol {

}

struct BaseStruct2: MyProtocol {

}

protocol ContainStringProtocol: Hashable {
    var string: String { get }
}

struct MyGenericStruct<T: MyProtocol>: ContainStringProtocol {
    var string: String
    var hashValue: Int {
        get {
            return string.hashValue
        }
    }

    init(string: String) {
        self.string = string
    }

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

extension Set where Element: ContainStringProtocol {
    func getFirstWith(string: String) -> Element? {
        return filter({$0.string == string}).first
    }
}

// Example
let set1: Set = [
    MyGenericStruct<BaseStruct1>(string: "Watermelon"),
    MyGenericStruct<BaseStruct1>(string: "Orange"),
    MyGenericStruct<BaseStruct1>(string: "Banana")
]
let output1 = set1.getFirstWith(string: "Orange")
print(output1!)

let set2: Set = [
    MyGenericStruct<BaseStruct2>(string: "Watermelon"),
    MyGenericStruct<BaseStruct2>(string: "Orange"),
    MyGenericStruct<BaseStruct2>(string: "Banana")
]
let output2 = set2.getFirstWith(string: "Orange")
print(output2!)

答案 1 :(得分:1)

详细

Swift 3.1,xCode 8.3.3

解决方案

import Foundation

struct MyStruct:Hashable {

    let string: String

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

    var hashValue: Int {
        return string.hash
    }
}

extension Set where Element == MyStruct {
    func getFirstWith(string: String) -> Element? {
        return filter({ $0.string == string }).first
    }
}

var set = Set<MyStruct>()
set.insert(MyStruct(string: "123"))
set.insert(MyStruct(string: "231"))
print(String(describing:set.getFirstWith(string: "123")))
print(String(describing:set.getFirstWith(string: "231")))

///////////////////////////////////

protocol MyProtocol {}

struct MyType1: MyProtocol {}
struct MyType2: MyProtocol {}

struct MyGenericStruct<T: MyProtocol>: Hashable {
    let string: String

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

    var hashValue: Int {
        return string.hash
    }
}

extension Set where Element == AnyHashable {
    func getFirstWith(string: String) -> Element? {


        return filter{ element -> Bool in
            if let _element = element as? MyGenericStruct<MyType1> {
                if  _element.string == string {
                    return true
                }
            }
            if let _element = element as? MyGenericStruct<MyType2> {
                if  _element.string == string {
                    return true
                }
            }
            return false
        }.first

    }
}

var set2 = Set<AnyHashable>()
set2.insert(MyGenericStruct<MyType1>(string: "abc"))
set2.insert(MyGenericStruct<MyType2>(string: "cba"))
print(String(describing: set2.getFirstWith(string: "abc")))
print(String(describing:set2.getFirstWith(string: "cba")))

结果

enter image description here

答案 2 :(得分:1)

根据要求在这里回答我自己的问题。

我无法弄清楚如何将其作为Set上的扩展程序。

相反,我们写了一个小实用函数。不像扩展那样清晰,但完成工作。它看起来像这样:

func getFirst<T: MyProtocol>(fromSet set: Set<MyGenericStruct<T>>, whereStringIs string: String) -> MyGenericStruct<T>? {
    return set.first(where: { $0.string == string })
}

正如其他人所说,MyGenericStruct需要是Hashable。