這是困擾包括我在內的許多開發人員的事情。假設我們有一個協議,它定義了我們應用于一個簡單類的下標。
protocol Cache {
subscript<Value>(_: String) -> Value? { get set }
}
class InMemoryCache: Cache {
private var cache: [String: Any] = [:]
subscript<Value>(key: String) -> Value? {
get {
cache[key] as? Value
}
set {
if let value = newValue {
cache[key] = value
} else {
cache.remove(key)
}
}
}
}
只要我們知道型別,它就可以正常作業:
cache["abc"] = 5
let x: Int? = cache["abc"]
但開發人員想要這樣做:
cache["abc"] = nil
哪個不會編譯,因為編譯器無法確定Value泛型型別。然而,這有效
cache["abc"] = nil as String?
我嘗試了很多東西,但它們都有缺點。諸如添加型別的第二個下標之類的事情Any。即使看起來像一個簡單的問題,似乎也沒有什么能很好地作業。
有沒有人找到處理的解決方案cache["abc"] = nil?
uj5u.com熱心網友回復:
您可以通過稍微改變您的協議要求來做到這一點。
讓協議需要一個不使用泛型的下標,并回傳一個Any?.
protocol Cache {
subscript(key: String) -> Any? { get set }
}
此下標將允許您執行以下操作:
cache["abc"] = 5
cache["abc"] = nil
let value = cache["abc"] // value is an `Any?`
但它不會讓你這樣做:
let number: Int? = cache["abc"] // error
所以,讓我們通過添加另一個下標來解決這個問題Cache。此下標等效于您的原始下標要求,只是它不需要設定器并將呼叫另一個下標(協議要求的下標):
extension Cache {
subscript<Value>(key: String) -> Value? {
self[key] as? Value
}
}
(如果你擔心這個下標會呼叫自己,就不用了。self[key]這里實際上呼叫的是另一個下標,而不是這個。你可以在 Xcode 中通過命令單擊[或]inself[key]來確認這一點,以跳轉到定義其他下標。)
然后,在您的類中實作所需的下標:
class InMemoryCache: Cache {
private var cache: [String: Any] = [:]
subscript(key: String) -> Any? {
get { cache[key] }
set { cache[key] = newValue }
}
}
這將允許編譯以下所有內容:
let cache = InMemoryCache()
cache["abc"] = 5
let x: Int? = cache["abc"]
cache["abc"] = nil
uj5u.com熱心網友回復:
有一種解決方法可以讓您獲得所需的輸出。
因為這是一本字典,所以你可以nil直接在你的InMemoryCache
class InMemoryCache: Cache {
private var cache: [String: Any] = [:]
subscript<Value>(key: String) -> Value? {
get {
cache[key] as? Value
}
set {
if let value = newValue {
cache[key] = value
} else {
cache[key] = nil // make nil directly here
}
}
}
}
因為這里Value是泛型型別。所以不能nil直接賦值。它必須具有特定的型別。
相反,你可以這樣做
let nilValue : Int? = nil // any type nil you want
cache["abc"] = nilValue
或在分配給字典之前直接將其轉換為任何 tupe 的 nil
cache["abc"] = (nil as String?)
它將重繪 存盤在密鑰中的任何值。
例子
// value
let nilValue : Int? = nil
var number : Int? = nil
var string : String? = nil
cache["abc"] = 5
number = cache["abc"] // Optional.some(5)
cache["abc"] = "abc"
number = cache["abc"] // nil
string = cache["abc"] // Optional.some("abc")
cache["abc"] = nilValue
number = cache["abc"] // nil
string = cache["abc"] // nil
uj5u.com熱心網友回復:
你在這方面遇到困難的原因是因為
cache["abc"] = nil
無法編譯。沒有足夠的資訊來推斷下標或可選值的通用型別。編譯器看到類似
cache<?>["abc"] = Optional<?>.none
它應該如何弄清楚用什么代替問號?
還有一個模棱兩可的。您的快取可以包含任何型別,甚至Optional. 當您分配nil給下標時,有人怎么知道您是要洗掉元素還是要在下Optional<Something>.none標處存盤 的實體?
當我發現自己以這種方式與語言抗爭時,我通常會嘗試退后一步,問我是否可能在做一些從根本上說不好的事情。我認為,在這種情況下,答案是肯定的。您試圖假裝某些內容的型別比實際情況更嚴格。
我認為你的 getter/setter 應該明確地采用 type 的值Any。它作業得更好,并且它的優點是它為用戶顯式記錄了Cache符合型別可以在其中存盤任何內容。
出于這個原因,我會說 TylerP 的解決方案是最好的。但是,我不會在擴展中創建下標,我會定義一個函式
extension Cache
{
func value<Value>(at key: String) -> Value?
{
self[key] as? Value
}
}
這樣做的原因是,當您有多個具有相似簽名的下標時,編譯器可能會感到困惑。通過上面的擴展,我可以符合Dictionary<String, Any>協議并且不需要新的類。
extension Dictionary: Cache where Key == String, Value == Any {}
var dict: [String : Any] = [:]
dict["abc"] = 5
let y: Int? = dict.value(at: "abc")
dict["abc"] = nil
顯然,如果您需要快取的參考語意,上述內容對您沒有用處。
uj5u.com熱心網友回復:
TylerP 的解決方案非常劃算。不過,為了完整起見,現在的代碼如下所示:
protocol Cache {
/// Handles when we need a value of a specific type.
subscript<Value>(_: String) -> Value? { get }
/// Handles getting and setting any value.
/// The getter is rarely used because the generic getter above
/// is used. Setting a value compiles because we don't care what
/// type is it. Setting a `nil` also compiles for the same reason.
subscript(_: String) -> Any? { get set }
}
class InMemoryCache: Cache {
private var cache: [String: Any] = [:]
subscript(key: String) -> Any? {
get { cache[key] }
set {
if let value = newValue {
cache[key] = value
} else {
remove(key)
}
}
}
subscript<Value>(key: String) -> Value? {
cache[key] as? Value
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/511217.html
標籤:迅速仿制药
上一篇:在when上使用泛型時型別不匹配
