2017-06-04 57 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應該如何使用的。 (你所要求的確實是不可能的,而且是有目的的。)寫下這個協議,但不是一個空洞的黑客;用你正在測試的內容填充它,然後將其視爲代碼結構的一個美麗部分!

相關問題