通过符合Swift 2中的协议来扩展类型化数组

时间:2015-12-26 00:37:48

标签: swift generics swift2 swift-extensions

我想扩展一个类型化数组Array<SomeType>,使其符合协议SomeProtocol。现在我知道你可以扩展一个类型化的数组,如下所示:

extension Array where Element: SomeType { ... }

你还可以扩展一个对象以符合这样的协议:

extension Array: SomeProtocol { ...  }

但我无法弄清楚使用类型数组符合协议的正确语法是什么,例如:

extension (Array where Element: SomeType): SomeProtocol { ... }

任何Swift 2专家都知道如何做到这一点?

3 个答案:

答案 0 :(得分:4)

您不能将大量逻辑应用于一致性。它要么符合要么不符合要求。但是,您可以对扩展应用一些逻辑。下面的代码可以轻松设置一致性的特定实现。哪个是重要的部分。

稍后将其用作类型约束。

protocol SomeProtocol {

    func foo()

}

这是你的协议

foo()

这是协议的扩展。在SomeProtocol扩展名中extension SomeProtocol { func foo() { print("general") } } 的实施会创建默认值。

Array

现在SomeProtocol使用foo()的默认实现符合foo()。所有数组现在都有extension Array : SomeProtocol {} 作为方法,这不是超级优雅的。但它没有做任何事情,所以它不会伤害任何人。

Array

现在很酷的东西: 如果我们使用Element的类型约束创建foo()的扩展名,我们可以覆盖extension Array where Element : SomeType { func foo() { print("specific") } } 的默认实现

let arrayOfInt = [1,2,3]
arrayOfInt.foo() // prints "general"

let arrayOfSome = [SomeType()]
arrayOfSome.foo() // prints "specific"

试验:

static void Main(string[] args)
{
    Thread.Sleep(DateTime.Today.AddDays(1) - DateTime.Now);
    Console.WriteLine("It's about midnight, I go to sleep");
}

答案 1 :(得分:2)

我自己一直在摆弄一些人,并且有一些方法可以模仿你正在寻找的行为。

方法#1

定义协议SomeType,作为Array<SomeType> SomeProtocol Array扩展名所涵盖类型的类型约束。其中后者包含您希望扩展的一些简洁方法的蓝图protocol SomeType { var intValue: Int { get } init(_ value: Int) func *(lhs: Self, rhs: Self) -> Self func +=(inout lhs: Self, rhs: Self) } extension Int : SomeType { var intValue: Int { return self } } extension Double : SomeType { var intValue: Int { return Int(self) } } /* Let's not extend 'Float' for now extension Float : MyTypes { var intValue: Int { return Int(self) } } */ protocol SomeProtocol { func foo<T: SomeType>(a: [T]) -> Int? }

Array

现在,您可以将SomeProtocol扩展为is,并使用T关键字,您可以声明您的通用SomeType(受{{1}约束} {} Self的元素使用is关键字属于同一类型,如果为true,则后跟显式转换:

extension Array : SomeProtocol {
    func foo<T: SomeType>(a: [T]) -> Int? {
        /* [T] is Self? proceed, otherwise return nil */
        if let b = self.first {
            if b is T && self.count == a.count {
                var myMultSum: T = T(0)

                for (i, sElem) in self.enumerate() {
                    myMultSum += (sElem as! T) * a[i]
                }
                return myMultSum.intValue
            }
        }
        return nil
    }
}

我们现在已扩展Array,其中SomeType元素的功能foo(...)已在协议SomeProtocol中进行了蓝图打印。

/* Tests */
let arr1d : [Double] = [1.0, 2.0, 3.0]
let arr2d : [Double] = [-3.0, -2.0, 1.0]

let arr1f : [Float] = [1.0, 2.0, 3.0]
let arr2f : [Float] = [-3.0, -2.0, 1.0]

func bar<U: SomeType> (arr1: [U], _ arr2: [U]) -> Int? {
    return arr1.foo(arr2)
}
let myInt1d = bar(arr1d, arr2d) // -4, OK

let myInt1f = bar(arr1f, arr2f)
    /* Compile time error: "Cannot convert value of type '[Float]'
       to expected argument type '[_]'"                            */

确定!我们预计这里的最终编译时错误为'Float'不符合SomeType协议。

方法#2

现在换另一种方法:我基于this excellent post by Milen Dzhumerov后面的泛型,这里适用于数组和一些不同的扩展方法示例。

对于此示例,我们正在为类型为ArrayDouble的{​​{1}}实现“通用”协议扩展,由类型约束协议Float表示

SomeType

使用结构将protocol SomeType { init(_ value: Int) init(_ value: Double) init(_ value: Float) func == (lhs: Self, rhs: Self) -> Bool } extension Double: SomeType {} extension Float: SomeType {} protocol GenericProtocol { typealias AbstractType : SequenceType func repeatNumberNumberManyTimes(arg: Int) -> AbstractType func removeRandomElement(arg: AbstractType) -> AbstractType func countNumberOf42s(arg: AbstractType) -> Int } 转发到GenericProtocol(此处符合AbstractType),并在后者中实施协议蓝图:

SequenceType

在另一个结构中实现struct SomeArrayProtocol<T: SequenceType> : GenericProtocol { private let _repeatNumberNumberManyTimes : (Int) -> T private let _removeRandomElement : (T) -> T private let _countNumberOf42s : (T) -> Int init<P : GenericProtocol where P.AbstractType == T>(_ dep : P) { _repeatNumberNumberManyTimes = dep.repeatNumberNumberManyTimes _removeRandomElement = dep.removeRandomElement _countNumberOf42s = dep.countNumberOf42s } func repeatNumberNumberManyTimes(arg: Int) -> T { return _repeatNumberNumberManyTimes(arg) } func removeRandomElement(arg: T) -> T { return _removeRandomElement(arg) } func countNumberOf42s(arg: T) -> Int { return _countNumberOf42s(arg) } } 蓝图的实际方法,其中泛型现在被约束为GenericProtocol类型约束(协议)。请注意,这部分是模仿你希望的冷杉(但直截了当地无法实现)形式SomeType

extension (Array where Element: SomeType): SomeProtocol { ... }

最后,一些测试:

struct SomeArrayGenericExtensions<T: SomeType> : GenericProtocol {
    typealias AbstractType = Array<T>
    func repeatNumberNumberManyTimes(arg: Int) -> [T] {
        return Array<T>(count: arg, repeatedValue: T(arg))
    }
    func removeRandomElement(arg: [T]) -> [T] {
        var output = [T]()
        let randElemRemoved = Int(arc4random_uniform(UInt32(arg.count-1)))
        for (i,element) in arg.enumerate() {
            if i != randElemRemoved {
                output.append(element)
            }
        }
        return output
    }
    func countNumberOf42s(arg: [T]) -> Int {
        var output = 0
        for element in arg {
            if element == T(42) {
                output++
            }
        }
        return output
    }
}

我有点不确定上面的方法2是否实际上对数组有一些实际的应用(在Milans报道中,他对待非序列类型,可能更有用);对于降压而言,没有那么多额外的帮助,这是相当多的工作。但是,它可以是一种有益的,非常有趣的练习:)

答案 2 :(得分:2)

在更新版本的Swift中,可以编写:

extension Array: SomeProtocol where Element == SomeType { ... }

不确定哪个版本的Swift成为可能,但以下内容适用于Swift 4.1

class SomeType { }

protocol SomeProtocol {
    func foo()
}

extension Array: SomeProtocol where Element == SomeType {
    func foo() {
        print("foo")
    }
}

let arrayOfSome = [SomeType()]
arrayOfSome.foo() // prints "foo"

let arrayOfInt = [1,2,3]
arrayOfInt.foo() // Will not compile: '[Int]' is not convertible to 'Array<SomeType>'

(我知道这个问题特别要求Swift 2,但我将其添加以供参考)