我正在实现一个函数ofType,该函数过滤掉给定类型的所有元素.
这是我的代码:
class Animal {}
class Mammal: Animal {}
class Monkey: Mammal {}
class Pig: Mammal {}
class Human: Mammal {}
extension Array {
func ofType<T>(_ metatype: T.Type) -> [T] {
return flatMap { type(of: $0) == metatype ? $0 as? T : nil }
// return flatMap { $0 as? T } // This is not working as the T is always the static type of the parameter, which is Animal in this example.
// return flatMap { $0 as? metatype } // This is not working either because of the grammar restriction.
}
}
let animals = [Monkey(), Pig(), Human(), Mammal(), Animal()]
func animalType() -> Animal.Type {
return Mammal.self
}
animals.ofType(animalType()).count // returns 1, expect to be 4.
Run Code Online (Sandbox Code Playgroud)
在Objc中,我可以isKindOf()用来检查对象是类的实例还是子类.还有在迅速类似的操作is和as,但经过他们的类型应该是静态类型,而不是一个动态类型值(例如,我可以写is Mammal,但没有is Mammal.self).
我不能使用类型参数T,因为在这个例子中,T等于Animal,这不是我想要的.
你对如何实现这个功能有任何想法吗?
这有效.只需as?在里面使用flatMap.如果动物可以被施放,它将被返回,否则nil返回并flatMap扔掉它
class Animal {}
class Mammal: Animal {}
class Monkey: Mammal {}
class Pig: Mammal {}
class Human: Mammal {}
extension Array {
func ofType<T>() -> [T]
{
return flatMap { $0 as? T }
}
}
let animals = [Monkey(), Pig(), Human(), Mammal(), Animal()]
let monkeys: [Monkey] = animals.ofType() // A one element array
let mammals: [Mammal] = animals.ofType() // A four element array
Run Code Online (Sandbox Code Playgroud)
如果显式键入输出数组,编译器可以从上下文中推断T,否则将T的类型作为参数传递,但不要在函数中使用它.
如果您希望能够动态检查类型,即您不知道要在编译时过滤的类型,则可以使用镜像.这是一个有点笨重的解决方案,但确实有效:
class Animal
{
func isInstance(of aType: Any.Type) -> Bool
{
var currentMirror: Mirror? = Mirror(reflecting: self)
while let theMirror = currentMirror
{
if theMirror.subjectType == aType
{
return true
}
currentMirror = theMirror.superclassMirror
}
return false
}
}
class Mammal: Animal {}
class Monkey: Mammal {}
class Pig: Mammal {}
class Human: Mammal {}
let animals = [Monkey(), Pig(), Human(), Mammal(), Animal()]
for aType in [Animal.self, Mammal.self, Monkey.self]
{
let result = animals.flatMap { $0.isInstance(of: aType) ? $0 : nil }
print("\(result)")
}
Run Code Online (Sandbox Code Playgroud)
打印:
[__lldb_expr_12.Monkey, __lldb_expr_12.Pig, __lldb_expr_12.Human, __lldb_expr_12.Mammal, __lldb_expr_12.Animal]
[__lldb_expr_12.Monkey, __lldb_expr_12.Pig, __lldb_expr_12.Human, __lldb_expr_12.Mammal]
[__lldb_expr_12.Monkey]
Run Code Online (Sandbox Code Playgroud)
编辑按照Sam在评论中的建议,我发现上述方法最好放在协议扩展中.
protocol TypeCheckable {}
extension TypeCheckable
{
func isInstance(of aType: Any.Type) -> Bool
{
var currentMirror: Mirror? = Mirror(reflecting: self)
while let theMirror = currentMirror
{
if theMirror.subjectType == aType
{
return true
}
currentMirror = theMirror.superclassMirror
}
return false
}
}
Run Code Online (Sandbox Code Playgroud)
然后,您可以通过使其符合协议,将功能添加到任何Swift类型.
class Animal: TypeCheckable { ... }
extension String: TypeCheckable {}
Run Code Online (Sandbox Code Playgroud)
就我个人而言,我认为@JeremyP\的使用建议Mirror是最好的;尽管我会对其进行一些调整:
/// Conditionally cast `x` to a given dynamic metatype value, taking into consideration\n/// class inheritance hierarchies.\nfunc conditionallyCast<T, U>(_ x: T, to destType: U.Type) -> U? {\n\n if type(of: x) is AnyClass && destType is AnyClass { // class-to-class\n\n let isCastable = sequence(\n first: Mirror(reflecting: x), next: { $0.superclassMirror }\n )\n .contains { $0.subjectType == destType }\n\n return isCastable ? (x as! U) : nil\n }\n\n // otherwise fall back to as?\n return x as? U\n}\nRun Code Online (Sandbox Code Playgroud)\n\n在这里,我们使用sequence(first:next:)从动态类型x到它可能具有的任何超类元类型来创建元类型序列(可能是我第一次使用该函数,看起来并不糟糕:P)。此外,我们正在回退做一个as?当我们知道我们没有进行类到类的转换时,我们会回退到进行转换,这允许函数也可以使用协议元类型。
然后你可以简单地说:
\n\nextension Sequence {\n func ofType<T>(_ metatype: T.Type) -> [T] {\n return flatMap { conditionallyCast($0, to: metatype) }\n }\n}\n\nprotocol P {}\nclass Animal {}\nclass Mammal: Animal {}\nclass Monkey: Mammal, P {}\nclass Pig: Mammal {}\nclass Human: Mammal, P {}\n\nlet animals = [Monkey(), Pig(), Human(), Mammal(), Animal()]\n\nlet animalType: Animal.Type = Mammal.self\nprint(animals.ofType(animalType)) // [Monkey, Pig, Human, Mammal]\n\nprint(animals.ofType(P.self)) // [Monkey, Human]\nRun Code Online (Sandbox Code Playgroud)\n\n另一种选择,假设您在 Apple 平台上(即可以访问 Objective-C 运行时),是使用 Objective-C 元类方法isSubclass(of:)来检查给定元类型是否相等,或者是其他:
import Foundation\n\n/// Conditionally cast `x` to a given dynamic metatype value, taking into consideration\n/// class inheritance hierarchies.\nfunc conditionallyCast<T, U>(_ x: T, to destType: U.Type) -> U? {\n\n let sourceType = type(of: x)\n\n if let sourceType = sourceType as? AnyClass,\n let destType = destType as? AnyClass { // class-to-class\n\n return sourceType.isSubclass(of: destType) ? (x as! U) : nil\n }\n\n // otherwise fall back to as?\n return x as? U\n}\nRun Code Online (Sandbox Code Playgroud)\n\n这是有效的,因为在 Apple 平台上,Swift 类构建在 Obj-C 类 \xe2\x80\x93 之上,因此 Swift 类的元类型是 Obj-C 元类对象。
\n| 归档时间: |
|
| 查看次数: |
646 次 |
| 最近记录: |