简体   繁体   English

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

[英]Extending a Collection where the Element type is generic (Swift)

It's simple to extend a Collection type in swift to have a custom function for particular Element types: 在swift中扩展Collection类型以使特定Element类型具有自定义函数很简单:

struct MyStruct {
    let string: String
}

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

But suppose your Element type is generic? 但是假设您的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
    }
}

It's unclear to me how to declare that my element is a generic. 我不清楚如何声明我的元素是通用的。

If I do understand the purpose, you probably need to have more implementation in your protocol and structure . 如果我确实理解了目的,您可能需要在protocolstructure实现更多实现。

First, element's protocol has to be hashable and equitable. 首先,元素的协议必须是可混合和公平的。

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

Therefore, MyGenericStruct will look like the following. 因此, 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
    }
}

Then, declare extension with constraints specified by a where clause for MyProtocol 然后,使用MyProtocol的where子句指定的约束声明扩展

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

Finally, let's do a test and see its result. 最后,让我们做一个测试,看看它的结果。

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

In my playground with Xcode 8, I can get Optional(MyGenericStruct(string: "Orange")) in log. 在我的Xcode 8操场上,我可以在日志中获得Optional(MyGenericStruct(string: "Orange"))

[UPDATED1] To make an Extension on Set<MyGenericStruct> only: [UPDATED1]仅在Set<MyGenericStruct>上进行Extension

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

[UPDATED2] In case to keep MyGenericStruct<T: MyProtocol> declaration as stated is necessary, another approach to implement Set extension: [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!)

Details 细节

Swift 3.1, xCode 8.3.3 Swift 3.1,xCode 8.3.3

Solution

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")))

Result 结果

在此输入图像描述

Answering my own question here on request. 根据要求在这里回答我自己的问题。

I was unable to figure out how to make this an extension on Set . 我无法弄清楚如何将它作为Set的扩展。

Instead, we wrote a little utility function. 相反,我们写了一个小实用函数。 Not as clear as a extension, but gets the work done. 不像扩展那样清晰,但完成工作。 It looks like this: 它看起来像这样:

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

As others have noted, MyGenericStruct needs to be Hashable. 正如其他人所说, MyGenericStruct需要是Hashable。

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

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