2017-06-04 100 views
0

假设我有一个类class C<T>如何测试一个对象是继承的还是泛型类的成员?

我想要写的函数f(_ a: Any) -> Bool,当a是类从C继承的成员(或为C本身),其返回true。我不在乎专业化:通过C<Int>,C<Double>,C<Whatever>都应该返回true

似乎像我应该能够只写a is Ca 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 

回答

0

我不认为语言功能存在为你做到这一点,因为它不是很有用,只是知道一个对象是一个子类或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() 
} 
+0

关于实用性的观点;仅仅因为我不知道'someObject'专用的类型并不意味着它从'C'继承的知识是无用的;例如,只知道某个对象(例如,在视图层次结构中的超级视图)属于'C'类型,可能意味着某些对象下面的视图的不变量可以保持不变。 –

+0

@BenPious如果你在一个视图层次结构中,只需使用'标签'。通过检查一个superview的标签是否有价值,你可以知道这是否是你正在寻找的视图。它们不够吗? – Sweeper

+0

任何人都可以选择与我为某些视图所做的相同的标记。此外,现在'C'的用户必须知道他们不能设置从'C'继承的类型实例的标签,否则会破坏'C'的实现细节。要清楚,作为'C'的成员足以知道不变量存在;这不是一些具体的事例。所以我想要测试一下;而且如我在问题结尾处所说的那样,似乎我不能用'C'(并且只有'C')符合'P'擦除泛型。 –

0

见转储的声明方式

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

+0

对不起,但我不明白这是如何回答这个问题。你能解释一下更多关于如何实现'f'函数与'dump'有关吗? – Sweeper

+0

@Sweeper转储是如何在swift中使用镜像的最简单方法。如果您将运行该代码段,您会看到,结果以可读的形式存在,并且很容易识别OP正在查找的内容。几行代码可以使结果成为计算机可读的:-) – user3441734

+0

'dump'打印到标准输出;如果你想在运行时测试它,它实际上是没用的。即使它确实给了你一个字符串来解析,但是对于我如何去掉“_TtGC14__lldb_expr_281CSi_”,这是'dump(d)' –

0

发挥写协议,规定你期待什么,这个测试是不是在所有黑客攻击;这正是Swift应该如何使用的。 (你所要求的确实是不可能的,而且是有目的的。)写下这个协议,但不是一个空洞的黑客;用你正在测试的内容填充它,然后将其视为代码结构的一个美丽部分!