在Swift中,我需要能够通用地使用API提供给我的各种Token<T>
。
请注意,我使用的是Swift 5.8。
API定义了以下内容(仅显示相关部分):
public struct Token<T> : Codable, Equatable, Hashable { }
public struct Application : Equatable, Hashable {
public let token: ApplicationToken?
}
public struct ActivityCategory : Equatable, Hashable {
public let token: ActivityCategoryToken?
}
public struct WebDomain : Equatable, Hashable {
public let token: WebDomainToken?
}
public typealias ApplicationToken = Token<Application>
public typealias ActivityCategoryToken = Token<ActivityCategory>
public typealias WebDomainToken = Token<WebDomain>
// Data the API gives me:
struct ActivitySelection {
public var applicationTokens: Set<ApplicationToken>
public var categoryTokens: Set<ActivityCategoryToken>
public var webDomainTokens: Set<WebDomainToken>
}
我试着这样做:
// My custom ActivityType:
enum ActivityType {
case Applications
case Categories
case WebDomains
}
// My API Extension
extension ActivitySelection {
func tokensFor<T>(activity:ActivityType) -> [Token<T>] {
let array: [Token<T>]
switch activity {
case .Applications:
array = Array(self.applicationTokens) as! [Token<T>]
case .Categories:
array = Array(self.categoryTokens) as! [Token<T>]
case .WebDomains:
array = Array(self.webDomainTokens) as! [Token<T>]
}
// sort before returning here removed for simplicity
return array
}
}
let selection = ActivitySelection() // returns with data
然后当我尝试使用我的扩展时:
let tokens = selection.tokensFor(activity: .Applications)
我得到这个错误:
XCode Error: Generic parameter 'T' could not be inferred
如果我尝试这个:
func tokensFor<T>(activity:Activities) -> [Token<T>] {
let array: [Token<T>]
switch activity {
case .Applications:
array = Array(self.applicationTokens)
case .Categories:
array = Array(self.categoryTokens)
case .WebDomains:
array = Array(self.webDomainTokens)
}
// sort before returning here
return array
}
我得到:
Cannot assign value of type '[ApplicationToken]' (aka 'Array<Token<Application>>') to type '[Token<T>]'
Cannot assign value of type '[ActivityCategoryToken]' (aka 'Array<Token<ActivityCategory>>') to type '[Token<T>]'
Cannot assign value of type '[WebDomainToken]' (aka 'Array<Token<WebDomain>>') to type '[Token<T>]'
我也试过这样做:
func tokensFor(activity:Activities) -> [Token<Any>] {
let array: [Token<Any>]
switch activity {
case .Applications:
array = Array(self.applicationTokens)
case .Categories:
array = Array(self.categoryTokens)
case .WebDomains:
array = Array(self.webDomainTokens)
}
// sort before returning here
return array
}
但我得到错误:
Cannot assign value of type '[ApplicationToken]' (aka 'Array<Token<Application>>') to type '[Token<Any>]'
Cannot assign value of type '[ActivityCategoryToken]' (aka 'Array<Token<ActivityCategory>>') to type '[Token<Any>]'
Cannot assign value of type '[WebDomainToken]' (aka 'Array<Token<WebDomain>>') to type '[Token<Any>]'
我该怎么做?我认为泛型是这个问题的解决方案。
由于Token<T>
已经是一个泛型类型,我如何使用它来通用地使用3种不同的令牌类型?
2条答案
按热度按时间0x6upsns1#
使用
enum
不起作用的原因是枚举值只能在运行时知道:如果
randomlyGenerateAnActivity
随机选择三种情况之一,那么tokens
的编译时类型是什么?编译器无法知道。一个解决方案是创建这样一个类型,一个 Package
Token<T>
的非泛型类型,用类似的东西替换Token<T>
中涉及T
的所有内容,除了T
被替换为Any
。由于您不想显示Token<T>
有哪些成员,因此我不能给予任何代码示例。另一种解决方案是让
tokensFor
接受一个 metatype 作为参数。现在
tokensFor
将返回的类型在编译时总是已知的。请注意,除了使用
if...else if
来检查类型,您还可以在协议中添加一个静态属性,以返回相应的密钥路径:然后你就可以从
self[keyPath: T.activitySelectionKeyPath]
得到tokensFor
中的数组。因为你在SwiftUI视图中显示token,你应该创建一个泛型
View
,并将该泛型参数传递给tokensFor
:然后你可以做三个这样的
SomeGenericView
:rbl8hiat2#
这个签名并不意味着你认为它的意思:
这意味着调用者可以选择任何可能存在的类型(String,Int,他们刚刚定义的随机结构,UIViewController,任何东西),并且这个函数承诺返回一个 Package 该类型的Token。这一切都将在编译时静态地决定。托肯不是这样的你也不是这个意思
相反,目标是返回一个只有在运行时才知道的Token类型。这是协议的标准用法。