【问题标题】:Set default or nil value to the generic type parameter of the function将默认值或 nil 值设置为函数的泛型类型参数
【发布时间】:2021-09-11 22:12:25
【问题描述】:

假设我有一个协议

protocol TestProtocol {
}

另外,有一个结构并从协议继承它。

struct StructOne: TestProtocol {
}

现在,我有一个视图控制器类并创建了一个通用函数来接受TestProtocol 类型对象的数组(这是一个通用参数)。这是用于 SDK API 调用的传递参数。

但是在一些API调用中,我不需要传递这个参数数组。所以,我只想在函数定义中设置 nil 值或默认空数组。

这是课程

class TestViewController: UIViewController {
//  func genericCall<T: TestProtocol>(param: [T] = []) { // Not work
//  func genericCall<T: TestProtocol>(param: [T]? = nil) { // Not work
  func genericCall<T: TestProtocol>(param: [T]?) {
    if param?.isEmpty == true {
      print("Empty Param Calling")
    } else {
      print("With Param Calling")
    }
  }
   
  override func viewDidLoad() {
    super.viewDidLoad()
    let param = [StructOne(), StructOne()]
    self.genericCall(param: param) // This one work
    self.genericCall(param: [] as [StructOne]) // This one also work. But want default value in function
    self.genericCall(param: nil) // Not work : Error - Generic parameter 'T' could not be inferred
//    self.genericCall() // Not work with default empty value : Error - Generic parameter 'T' could not be inferred
  }
}

我收到此编译时错误:无法推断通用参数“T”

我可以在函数调用期间设置一个空数组。这是提到here

我还检查了这个link,如果只有 T 类型,它允许设置 nil 值,但这里是 T ([T]) 的数组。

有没有办法设置默认的 nil 值或任何其他方式来设置默认的空数组?这样我们就可以避免向每个函数调用传递一个空数组。

更新:

我不能这样使用它。由于 SDK 函数调用不允许我传递参数值。

func genericCall(param: [TestProtocol] = []) {
    // param: Not allowed me to pass to the sdk call function.
    if param.isEmpty == true {
        print("Empty Param Calling")
    } else {
        print("With Param Calling")
    }
}

注意:这是一个演示代码。实际上,我正在使用其中一个 SDK,所以我无法在协议中进行更多更改。

【问题讨论】:

  • 你为什么在这里使用泛型?为什么不直接声明func genericCall(param:[TestProtocol]?)
  • 如果您确实需要泛型(即,这只是问题的一个简单示例),但是泛型类中的泛型函数,然后您可以创建该类的专用实例并T 不需要推断
  • @Paulw11 我使用了你的方法,这是我的第一次尝试,但是当我将此参数值传递给 SDK 函数时,调用它会抛出错误 Protocol as a type cannot conform to the protocol itself

标签: ios swift generics protocols


【解决方案1】:

您可以创建一个泛型方法,将您的泛型类型限制为RangeReplaceableCollection,并将其Element 限制为您的TestProtocol。它起作用的原因是RangeReplaceableCollection 需要符合它的协议来提供一个空的初始化器:


protocol TestProtocol { }

struct StructOne: TestProtocol { }

class TestViewController: UIViewController {

    func genericCall<T: RangeReplaceableCollection>(param: T = .init()) -> T where T.Element:  TestProtocol {
        if param.isEmpty {
          print("Empty Param Calling")
        } else {
          print("With Param Calling")
        }
        return param
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        let result: [StructOne] = genericCall() // This one work
        print(result)
    }
}

【讨论】:

  • 感谢您的回答。但是当我调用 genericCall() 时。它给了我错误实例方法'实例方法'genericCall(param :)'要求'_.Element'符合'TestProtocol''我错过了什么吗?
  • @RajaKishan 您可能忘记添加返回类型T。请注意,您还需要显式设置结果类型
  • 谢谢。因为我的功能不返回任何东西。所以这样做可能会让下一个开发者感到困惑,为什么会这样返回。所以目前我正在选择其他答案。感谢这种方式
【解决方案2】:

nil 在这种情况下过于宽泛,因为编译器无法推断它应该将 nil 应用到哪个 [T]?

您需要在此处明确指定 Optional 通用参数:

self.genericCall(param: [StructOne]?.none)

【讨论】:

  • 感谢您的回答。我可以通过您通过 StructOne 的第一种方法来解决此问题。但是在使用第二种方法时出错。 Protocol 'TestProtocol' as a type cannot conform to the protocol itself
  • @RajaKishan 我会从答案中删除它,我没有机会编译它,但是由于您遇到错误,这意味着带有泛型的协议存在尚不能在 Swift 中工作.
  • 没问题。但是有什么方法可以在函数定义中设置值,而不是在函数调用时传递这个值[StructOne]?.none
  • @RajaKishan 你在这里有点不走运,就像Swift protocols don't conform to themselves,所以如果你想适应这个,你可能需要改变你的设计
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-24
  • 2016-11-14
  • 2018-02-18
相关资源
最近更新 更多