如何测试一个对象是继承的还是泛型类的成员?
假设我有一个类class C<T>
。如何测试一个对象是继承的还是泛型类的成员?
我想要写的函数f(_ a: Any) -> Bool
,当a
是类从C
继承的成员(或为C
本身),其返回true。我不在乎专业化:通过C<Int>
,C<Double>
,C<Whatever>
都应该返回true
。
它似乎像我应该能够只写a is C
或a as? C != nil
作为函数体,但是,这些都没有在操场编译; swiftc抱怨说:“通用参数'T'不能推断为'C < _>'”。如果我在C
内写作f
作为实例方法,C
隐含地为C<T>
,所以编写let c = C<Int>(); c.f(C<Double>())
返回false
。
我可以通过编写一个协议P
,其中C
符合,然后测试,但我不认为这是一个很好的解决方案;这只是一个黑客。
有没有办法做到这一点?
这里的一切我写尝试这种代码:
class C<T> {
func test(_ a: Any) -> (Bool, Bool) {
return (type(of: a) == C.self, a is C)
}
}
class D: C<Double> { }
let d = D()
func f(_ a: Any) -> Bool {
return a is C // generic parameter 'T' could not be inferred in cast to 'C<_>'
}
d.test(C<Int>()) // false, false
// bad solution
protocol P { }
extension C: P { }
d is P // true
我不认为语言功能存在为你做到这一点,因为它不是很有用,只是知道一个对象是一个子类或C
本身,而不知道泛型类型参数。
我的意思是,你知道后会做什么,是的,someObj
确实是C<Something>
类型,但不是Something
是什么?你无法用someObject
做任何事情。你不能施放它,所以你不能访问它的任何成员。
斯威夫特的仿制药是非常很严格,不像Java中,谁只是抛出泛型出在运行窗口...
如果你坚持要做到这一点,你唯一的选择就是使用你提到的黑客。
或者,如果你只是想检查a
是否C
本身,而不是它的任何子类,你可以使用这个(只是为了好玩):
func f(a: Any) -> Bool {
let regex = try! NSRegularExpression(pattern: "C<.+>")
let typeString = String(describing: type(of: a))
let range = NSRange(location: 0, length: typeString.characters.count)
let matchRange = regex.rangeOfFirstMatch(in: typeString, range: range)
return range.toRange() == matchRange.toRange()
}
见转储的声明方式
func dump<T, TargetStream where TargetStream : TextOutputStream>(_ value: T, to target: inout TargetStream, name: String? = default, indent: Int = default, maxDepth: Int = default, maxItems: Int = default) -> T
并检查它所产生
class C<T>{}
class D:C<Double>{}
let c = C<Void>()
let d = D()
dump(c)
dump(d)
是,转储是Swift标准库的一部分......它使用反射(对象镜像)来产生结果。
UPDATE
我创建了一个名为dumptest一个文件主要CLI项目。迅速
//
// main.swift
// dumptest
//
class C<T>{}
class D:C<Double>{}
class E:D{}
let c = C<Void>()
let d = D()
let e = E()
var tc = ""
var td = ""
var te = ""
dump(c, to: &tc)
dump(d, to: &td)
dump(e, to: &te)
print("type info about c:", tc)
print("type info about d:", td)
print("type info about e:", te)
运行它的程序报
type info about c: - dumptest.C<()> #0
type info about d: - dumptest.D #0
- super: dumptest.C<Swift.Double>
type info about e: - dumptest.E #0
- super: dumptest.D
- super: dumptest.C<Swift.Double>
Program ended with exit code: 0
为了解析字符串变量检查计算器,或提出新问题......
让有
import Cocoa
let v = NSView()
dump(v)
转储值v,我们有
- <NSView: 0x100a022a0> #0
- super: NSResponder
- super: NSObject
Program ended with exit code: 0
如果你需要的东西“更加复杂”,你可以用 Mirror
对不起,但我不明白这是如何回答这个问题。你能解释一下更多关于如何实现'f'函数与'dump'有关吗? – Sweeper
@Sweeper转储是如何在swift中使用镜像的最简单方法。如果您将运行该代码段,您会看到,结果以可读的形式存在,并且很容易识别OP正在查找的内容。几行代码可以使结果成为计算机可读的:-) – user3441734
'dump'打印到标准输出;如果你想在运行时测试它,它实际上是没用的。即使它确实给了你一个字符串来解析,但是对于我如何去掉“_TtGC14__lldb_expr_281CSi_”,这是'dump(d)' –
发挥写协议,规定你期待什么,这个测试是不是在所有黑客攻击;这正是Swift应该如何使用的。 (你所要求的确实是不可能的,而且是有目的的。)写下这个协议,但不是一个空洞的黑客;用你正在测试的内容填充它,然后将其视为代码结构的一个美丽部分!
关于实用性的观点;仅仅因为我不知道'someObject'专用的类型并不意味着它从'C'继承的知识是无用的;例如,只知道某个对象(例如,在视图层次结构中的超级视图)属于'C'类型,可能意味着某些对象下面的视图的不变量可以保持不变。 –
@BenPious如果你在一个视图层次结构中,只需使用'标签'。通过检查一个superview的标签是否有价值,你可以知道这是否是你正在寻找的视图。它们不够吗? – Sweeper
任何人都可以选择与我为某些视图所做的相同的标记。此外,现在'C'的用户必须知道他们不能设置从'C'继承的类型实例的标签,否则会破坏'C'的实现细节。要清楚,作为'C'的成员足以知道不变量存在;这不是一些具体的事例。所以我想要测试一下;而且如我在问题结尾处所说的那样,似乎我不能用'C'(并且只有'C')符合'P'擦除泛型。 –