【问题标题】:How to pass nil as optional generic function argument如何将 nil 作为可选的通用函数参数传递
【发布时间】:2018-10-01 20:32:52
【问题描述】:

我有一个功能如下:

func request<D: Decodable>(from urlString: String,
                           useToken: Bool = false,
                           requestType: RequestType = .get,
                           body: Data? = nil,
                           expecting type: D.Type? = nil,
                           completion: @escaping (Result<D?>) -> Void)

是否可以这样做:request(..., expecting: nil)func request&lt;D: Decodable&gt;(... expecting type: D.Type? = nil)

我认为我已经达到了使用泛型的限制,因为当我这样做时,我得到的编译错误与我正在处理的代码完全无关,所以我认为编译器可能会感到困惑.

当我使用函数时,如:request(from: "https:..", requestType: .post, body: body),编译器报错Enum element 'post' cannot be referenced as an instance member

我的一些 API 请求在正文中没有返回任何内容,因此我试图找到一种方法来使用我设置的这个通用函数来表达这一点

【问题讨论】:

  • func request&lt;D: Decodable&gt;(expecting type: D.Type? = nil)
  • @LeoDabus 我也试过了,但是编译器会弄糊涂
  • 并致电request()
  • 如果你没有传递类型,你需要强制转换结果或显式设置结果类型
  • 你必须帮助编译器推断D类型

标签: swift generics optional


【解决方案1】:

这里的根本问题是你想要的类型是 Void,但 Void 不是 Decodable,你不能让它成为 Decodable,因为非名义类型(如 Void)不能扩展。这只是 Swift 的当前限制。

解决这个问题的正确方法是重载。创建两个方法:

// For values
func request<D: Decodable>(from urlString: String,
                           useToken: Bool = false,
                           requestType: RequestType = .get,
                           body: Data? = nil,
                           expecting type: D.Type,
                           completion: @escaping (Result<D>) -> Void) {}

// For non-values
func request(from urlString: String,
             useToken: Bool = false,
             requestType: RequestType = .get,
             body: Data? = nil,
             completion: @escaping (Error?) -> Void) {}

创建另一个将请求转换为数据并且两者都可以调用的共享方法:

func requestData(from urlString: String,
                 useToken: Bool = false,
                 requestType: RequestType = .get,
                 body: Data? = nil,
                 completion: @escaping (Result<Data>) -> Void) {}

您的解码请求函数现在会将.success(Data) 转换为D。您的非解码请求函数将丢弃数据(或者如果您对此很迂腐,可能会确保它为空),并调用完成处理程序。

如果您希望您的代码更加并行,以便它始终传递结果而不是错误?,那么您仍然可以通过调整签名来实现:

func request(from urlString: String,
             useToken: Bool = false,
             requestType: RequestType = .get,
             body: Data? = nil,
             completion: @escaping (Result<Void>) -> Void) {}

但重载仍然是这里的答案。


(旧答案) 在这里传递nil 没有问题,只要可以以某种方式推断出D。但是必须有一种方法来推断D。例如,以下应该没问题:

request(from: "") { (result: Result<Bool?>) in
    print(result)
}

这样不好:

request(from: "") { (result) in
    print(result)
}

因为在这种情况下,无法确定 D 是什么。

也就是说,鉴于您的目标,您不希望 Type 无论如何都是可选的。正如您所说,有时结果是“什么都不返回”。 “不返回任何内容”的正确类型是 Void,而不是 nil。

func request<D: Decodable>(from urlString: String,
                           useToken: Bool = false,
                           body: Data? = nil,
                           expecting type: D.Type = Void.self, // <<----
                           completion: @escaping (Result<D>) -> Void)

(我假设您想要Result&lt;D&gt; 而不是Result&lt;D?&gt;,但根据您的具体用例,两者都可能是正确的。)

Void 是 Swift 中的普通类型。它是一个只有一个值的类型:(),空元组。

【讨论】:

  • 感谢您的帮助!当我使用Void 时,requestType 函数参数出现以下错误:Enum element 'post' cannot be referenced as an instance member。有任何想法吗?还值得一提的是,如果我完全按照您的建议使用 Void(默认参数),我会收到一条错误消息:Default argument value of type 'Void.Type' cannot be converted to type 'D.Type'
  • 这几乎肯定是无关的。我怀疑它与RequestType 有关(尽管它可能只是让编译器感到困惑的其他东西;您没有提供有关RequestType 的任何信息)。简化您的签名,直到它编译;然后再添加一个元素以确保您知道原因是什么。
  • RequestType 只是一个简单的枚举。如果我删除它,我会收到以下错误:Generic parameter 'D' could not be inferred。 (这是当我使用 Void 作为您建议的默认参数时)
  • “无法推断出通用参数'D'”您是否阅读了我对您的问题的评论并检查了我发布给您的链接?
【解决方案2】:

这对我在操场上很好用

 let t = testStruct.init()
   let t2 : testStruct? = nil
   test(t)
   testOptional(t)
   testOptional(t2)

func test<T: testProtocol>(_ para: T){
    print(para.id())
}
func testOptional<T: testProtocol>(_ para: T?){
    if let p = para{
        print(p.id())
    }
}
protocol testProtocol {
    func id() -> String
}
struct testStruct{

}
extension testStruct : testProtocol {
    func id() -> String {
        return "hello"
    } 
}

但你不能只调用 testOptional()。它必须被传递一些东西,甚至是一个 nil 可选的,这样才能推断出类型。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-22
    • 1970-01-01
    • 2014-06-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多