你如何编写一个通用函数来在Swift中进行条件向下转换?

时间:2015-08-31 23:45:00

标签: swift generics functional-programming

我的目标是编写一些可以将[AnyObject]转换为Result<[SomeObject], NSError>的内容,以便在使用混合的Swift / Obj-C代码库时轻松进行安全和可链接的转换。条件转发运算符as?似乎支持这一点,但是我无法获得利用该行为的通用函数。我已经简化了我遇到的问题:

class A { }

let obj = A()

let array: [AnyObject] = [obj]

func cast<T, U>(x: T, type: U.Type) -> U? {
    if let x = x as? U {
        return x
    } else {
        return nil
    }
}

// This works
if let array = array as? [A] {
    println(array)
}

// This works
println(cast(obj, A.self))

// This doesn't
println(cast(array, [A].self))

2 个答案:

答案 0 :(得分:2)

注意:这适用于Swift 2.0,我不知道1.2,试试看

如果您想要执行此类操作,则必须使用cast方法重载SequenceType方法:

func cast<T : SequenceType, U : SequenceType>(x: T, type: U.Type) -> [U.Generator.Element]? {
    let y = x.map{ $0 as? U.Generator.Element }
    if y.contains({ $0 == nil }) {
        return nil
    } else {
        return y.flatMap{ $0 }   // Swift 1.2 : y.map{ $0! }
    }
}

编辑:根据你的编辑改变

答案 1 :(得分:1)

通常情况下,即使GenericType<A> as? GenericType<B>B的子类型,Swift也不支持A强制转换。 Array<A> as? Array<B>只是为了方便我们。

有一个未记录的内部内置函数:

func _arrayConditionalDownCastElements<SourceElement, TargetElement>(a: Array<SourceElement>) -> [TargetElement]?

根据我的假设,当我们执行someArray as? [B]时,Swift会隐式调用此函数。但是,使用泛型类型,就像你的情况一样,Swift编译器不能将它绑定到_arrayConditionalDownCastElements,因为它在编译时是不可预测的。

无论如何,您可以手动调用它,并将cast实现为重载函数:

func cast<T,U>(x:T, _: U.Type) -> U? {
    return x as? U
}

func cast<T,U>(x:[T], _: [U].Type) -> [U]? {
   return _arrayConditionalCast(x)
}

同样,_dictionaryDownCastConditionalDictionary_setDownCastConditionalSet

func _dictionaryDownCastConditional<BaseKey, BaseValue, DerivedKey, DerivedValue>(source: Dictionary<BaseKey, BaseValue>) -> Dictionary<DerivedKey, DerivedValue>?
func _setDownCastConditional<BaseValue, DerivedValue>(source: Set<BaseValue>) -> Set<DerivedValue>?

使用此:

func cast<TKey,TValue, UKey, UValue>(x:[TKey: TValue], _: [UKey:UValue].Type) -> [UKey: UValue]? {
    return _dictionaryDownCastConditional(x)
}

func cast<T, U>(x: Set<T>, _: Set<U>.Type) -> Set<U>? {
    return _setDownCastConditional(x)
}

同样,他们无证件。使用它们需要您自担风险:)