Swift:是否可以发现数组中元素的类型并用于指定泛型类型参数?

问题描述 投票:1回答:2

我有一个名为APIRequest的协议,其关联类型为ResponseType和一个解码函数。这个例子并不完整,但是我相信这些是问题的唯一相关部分。

还有一个名为ArrayResponse的结构,用于表示网络响应何时以items个不同对象的数组形式返回(取决于特定的APIRequestResponseTypetotalItems

protocol APIRequest {
    associatedtype ResponseType: Codable

    /// Decodes the request result into the ResponseType
    func decode(_: Result<Data, APIError>) throws -> ResponseType
}

struct ArrayResponse<T>: Codable where T: Codable {
    let items: [T]
    let totalItems: Int
}

这是一个遵循APIRequest协议并将其ResponseType指定为Brand的结构的示例,这是一个Codable结构,表示从服务器返回的品牌数据。

struct BrandRequest: APIRequest {
    typealias ResponseType = Brand
}
struct Brand: Codable {
    var brandID: Int
    var brandName: String?
}

BrandRequest用于从服务器获取单个Brand,但是我也可以获取Brand的数组(由上面的ArrayResponse表示,因为Brand是许多不同类型的类型之一都遵循相同的模式),使用BrandsRequest,将ResponseType指定为Brand s的数组。

struct BrandsRequest: APIRequest {
    typealias ResponseType = [Brand]
}

而不是在每个遵循decode的结构中提供APIRequest函数,我决定在协议扩展中进行默认实现,因为它们都遵循相同的解码。

取决于ResponseType是数组(例如[Brand],还是单个项目,例如Brand,我使用了另一个版本的decode函数。这对于单个项目效果很好,但是对于项目数组,我想研究一下Array,发现其元素的类型,并使用它来检查result.decoded()是否被解码为该特定类型的ArrayResponse<>

因此,例如,如果我创建一个BrandsRequest,我想要这个顶部的decode函数,该函数对数组进行解码以返回(try result.decoded() as ArrayResponse<Brand>).items,而Brand是一个不同的结构(例如Product,[C0 ]等),具体取决于此函数接收的数组中Element的类型。此示例包含一些未编译的代码,作为我尝试获取Customer并将其用作通用参数的方法,但是当然不起作用。我也不能简单地将elementType作为通用参数传递,因为编译器告诉我:Codable

所以我的问题是:

  1. 是否有一种方法可以捕获数组中要在Value of protocol type 'Codable' (aka 'Decodable & Encodable') cannot conform to 'Decodable'; only struct/enum/class types can conform to protocols中使用的元素的类型?
  2. 是否有更好的方法来ArrayResponse<insert type here>返回类似于decode的项的数组的网络响应与ArrayResponse的单个项的响应?
  3. Brand

附录:我想到的另一种方法是更改​​extension APIRequest where ResponseType == Array<Codable> { func decode(_ result: Result<Data, APIError>) throws -> ResponseType { let elementType = type(of: ResponseType.Element.self) print(elementType) return (try result.decoded() as ArrayResponse<elementType>).items } } extension APIRequest { func decode(_ result: Result<Data, APIError>) throws -> ResponseType { return try result.decoded() as ResponseType } } 以使用T作为数组类型,而不是元素类型:

ArrayResponse<>

然后像这样简化数组解码:

struct ArrayResponse<T>: Codable where T: Codable {
    let items: T
    let totalItems: Int
}

但是,编译器给了我这两个错误:extension APIRequest where ResponseType == Array<Codable> { func decode(_ result: Result<Data, APIError>) throws -> ResponseType { return (try result.decoded() as ArrayResponse<ResponseType>).items } } 'ArrayResponse' requires that 'Decodable & Encodable' conform to 'Encodable'


附录2:

如果将另一个关联类型添加到Value of protocol type 'Decodable & Encodable' cannot conform to 'Encodable'; only struct/enum/class types can conform to protocols以定义数组中Element的类型,则可以使所有工作正常进行和编译:
APIRequest

然后将我的数组protocol APIRequest { associatedtype ResponseType: Codable associatedtype ElementType: Codable /// Decodes the request result into the ResponseType func decode(_: Result<Data, APIError>) throws -> ResponseType } 更改为使用decode而不是ElementType

Codable

但是我必须在每个符合extension APIRequest where ResponseType == Array<ElementType> { func decode(_ result: Result<Data, APIError>) throws -> ResponseType { return (try result.decoded() as ArrayResponse<ResponseType>).items } } 的结构中提供ElementType,包括对APIRequest多余且未使用的单个请求。对于数组请求,它只是数组ResponseType内的值,也感觉重复:

ResponseType

我的问题的症结在于,我想在struct BrandRequest: APIRequest { typealias ResponseType = Brand typealias ElementType = Brand } struct BrandsRequest: APIRequest { typealias ResponseType = [Brand] typealias ElementType = Brand } 数组中发现Brand类型,并将其用于[Brand]解码。

我有一个名为APIRequest的协议,其关联类型为ResponseType和一个解码函数。这个例子并不完整,但是我相信这些是问题的唯一相关部分。 ...

arrays swift generics protocols
2个回答
1
投票

可能值得创建ArrayResponse,然后提供ArrayRequest的默认实现,例如:


0
投票

我怀疑这是对协议的滥用。 PAT(具有关联类型的协议)都是为了向现有类型添加更多功能,目前尚不清楚这样做。相反,我认为您有一个泛型问题。

© www.soinside.com 2019 - 2024. All rights reserved.