协议类型“Encodable”的值不能符合“Encodable”;只有结构/枚举/类类型可以符合协议
Posted
技术标签:
【中文标题】协议类型“Encodable”的值不能符合“Encodable”;只有结构/枚举/类类型可以符合协议【英文标题】:Value of protocol type 'Encodable' cannot conform to 'Encodable'; only struct/enum/class types can conform to protocols 【发布时间】:2020-06-07 22:34:17 【问题描述】:我有以下 Swift 代码
func doStuff<T: Encodable>(payload: [String: T])
let jsonData = try! JSONEncoder().encode(payload)
// Write to file
var things: [String: Encodable] = [
"Hello": "World!",
"answer": 42,
]
doStuff(payload: things)
导致错误
Value of protocol type 'Encodable' cannot conform to 'Encodable'; only struct/enum/class types can conform to protocols
如何解决?我想我需要更改things
的类型,但我不知道该怎么做。
附加信息:
如果我将 doStuff
更改为不通用,我只会在该函数中遇到同样的问题
func doStuff(payload: [String: Encodable])
let jsonData = try! JSONEncoder().encode(payload) // Problem is now here
// Write to file
【问题讨论】:
你的目标是什么?你想达到什么目的?您打算将字典值作为协议而不是类型做什么? @matt 我最初的问题可能不够清楚。我需要将参数序列化为doStuff
。如果我将签名更改为您的建议,我将遇到与我描述的完全相同的问题,就在我调用编码函数时。
【参考方案1】:
Encodable
不能用作带注释的类型。它只能用作通用约束。而JSONEncoder
只能编码具体类型。
功能
func doStuff<T: Encodable>(payload: [String: T])
是正确的,但您不能使用[String: Encodable]
调用该函数,因为协议不能符合自身。这正是错误消息所说的内容。
主要问题是things
的真实类型是[String:Any]
,而Any
无法编码。
您必须使用JSONSerialization
序列化things
或创建一个辅助结构。
【讨论】:
谢谢。我害怕这个。拥有一个辅助结构极大地限制了我,因为我只想编码和转储任意数据(符合 Encodable 协议)。protocol
的全部意义不在于它声明了一个函数以便可以随时调用它吗?如果它有像open func encode<T>(_ value: T) throws -> Data where T : Encodable
这样的函数,我希望它是可调用的。 Swift
还需要什么?这完全扼杀了使用接口的整个想法。接下来是什么,description
也需要一个特定的实例?
@vadian 我仍然无法理解。我正在存储Encodable
类型的东西,它有一个函数encode
(它指定了这个合约)。这意味着所有实例都有一个函数encode
。这意味着我应该可以调用它。
@vadian 这将是protocol
的全部意义,因此我们不必在运行时关心单个具体类型,并且仍然调用它声明的函数。如果某个东西是Encodable
,它已经实现了encode
(它必须)。这意味着我不能写if let encodable = anyObject as? Encodable try JSONEncoder().encode(payload)
,这对我来说很荒谬。
感谢您的解释,但来自其他语言的这种语言功能(限制)对我来说听起来绝对荒谬。 [String: Encodable] because a protocol cannot conform to itself.
通过指定 [String: Encodable]
(至少在 c# 或 typescript 等语言中)我们不是要求接口(或本例中的协议)符合自身,我们不能创建接口的实例,所以值只能是符合协议的 struct/class/enum 的实例。【参考方案2】:
您可以将where
关键字与Value
类型结合使用,如下所示:
func doStuff<Value>(payload: Value) where Value : Encodable
...
【讨论】:
这成功了!!!不需要辅助结构 XD【参考方案3】:您正在尝试使T
符合Encodable
,如果T == Encodable
则这是不可能的。协议不符合自身。
你可以试试:
func doStuff<T: Hashable>(with items: [T: Encodable])
...
【讨论】:
字典键不是问题。这是价值 (Encodable
)。当我将它传递给另一个函数时,我遇到了同样的问题。例如一个json编码器。我已经更新了问题以反映这一点。【参考方案4】:
通过协议扩展,您可以尝试做的事情:
protocol JsonEncoding where Self: Encodable
extension JsonEncoding
func encode(using encoder: JSONEncoder) throws -> Data
try encoder.encode(self)
extension Dictionary where Value == JsonEncoding
func encode(using encoder: JSONEncoder) throws -> [Key: String]
try compactMapValues
try String(data: $0.encode(using: encoder), encoding: .utf8)
Dictionary
中可能存在的每种类型都需要符合我们的JsonEncoding
协议。您使用了String
和Int
,所以这里有一些扩展来增加这两种类型的一致性:
extension String: JsonEncoding
extension Int: JsonEncoding
这是你的代码做你想做的事:
func doStuff(payload: [String: JsonEncoding])
let encoder = JSONEncoder()
do
let encodedValues = try payload.encode(using: encoder)
let jsonData = try encoder.encode(encodedValues)
// Write to file
catch
print(error)
var things: [String: JsonEncoding] = [
"Hello": "World!",
"answer": 42
]
doStuff(payload: things)
您没有询问解码,所以我没有在这里解决。您要么必须知道什么类型与什么键相关联,要么必须创建一个尝试解码值的顺序(应该将1
转换为Int
或Double
。 ..)。
我在没有质疑你的动机的情况下回答了你的问题,但对于你正在尝试做的事情,可能有一个更好、更“迅速”的解决方案......
【讨论】:
不错的解决方案,@DudeOnRock以上是关于协议类型“Encodable”的值不能符合“Encodable”;只有结构/枚举/类类型可以符合协议的主要内容,如果未能解决你的问题,请参考以下文章
类型 '' 不符合协议 'Decodable'/'Encodable'
如何使用Alamofire Router来组织API调用?(swift Alamofire5)