【问题标题】:Is there no default(T) in Swift?Swift 中没有 default(T) 吗?
【发布时间】:2023-04-09 19:18:02
【问题描述】:

我正在尝试将 Swift 书中的 Matrix 示例移植为通用的。

这是我目前得到的:

struct Matrix<T> {
    let rows: Int, columns: Int
    var grid: T[]

    init(rows: Int, columns: Int, repeatedValue: T) {
        self.rows = rows
        self.columns = columns

        grid = Array(count: rows * columns, repeatedValue: repeatedValue)
    }

    func indexIsValidForRow(row: Int, column: Int) -> Bool {
        return row >= 0 && row < rows && column >= 0 && column < columns
    }

    subscript(row: Int, column: Int) -> T {
        get {
            assert(indexIsValidForRow(row, column: column), "Index out of range")
            return grid[(row * columns) + column]
        }
        set {
            assert(indexIsValidForRow(row, column: column), "Index out of range")
            grid[(row * columns) + column] = newValue
        }
    }
}

请注意,我必须将 repeatedValue: T 传递给构造函数。

在 C# 中,我将只使用 default(T),这将是 0 用于数字,false 用于布尔值,null 用于引用类型。我知道 Swift 不允许在非可选类型上使用 nil,但我仍然很好奇是否传递显式参数是唯一的方法,或者我是否有 some 等效于 default(T)在那里。

【问题讨论】:

  • 我认为post 会有所帮助。

标签: swift generics


【解决方案1】:

没有。 Swift 强制你指定默认值,就像你处理变量和字段一样。 Swift 有默认值概念的唯一情况是可选类型,它是nil (Optional.None)。

【讨论】:

    【解决方案2】:

    一个不确定的“是”。您可以使用协议约束来指定泛型类或函数仅适用于实现默认 init 函数(无参数)的类型的要求。这样做的后果很可能很糟糕(它不会像你想象的那样工作),但它是最接近你所要求的东西,可能比“否”的答案更接近。

    对我来说,我个人发现这在开发新的泛型类期间很有帮助,然后最终我删除了约束并修复了剩余的问题。仅要求可以采用默认值的类型将限制泛型数据类型的实用性。

    public protocol Defaultable
    {
      init()
    }
    
    struct Matrix<Type: Defaultable>
    {
      let rows: Int
      let columns: Int
      var grid: [Type]
    
      init(rows: Int, columns: Int)
      {
        self.rows = rows
        self.columns = columns
    
        grid = Array(count: rows * columns, repeatedValue: Type() )
      }
    }
    

    【讨论】:

    • 我喜欢这种方法。我有一本带有Set 值的字典。我提供extension Dictionary where Value: Defaultable {…},我可以提供一个实现:subscript(_ key: Key) -&gt; Value {…}一个非可选的返回类型,这极大地简化了使用字典的代码,并为Dictionary 提供了我在这种情况下真正想要的语义.
    • 请注意,因为repeatingValue 不是@autoclosure,如果Type 是引用类型,您的数组将被相同的对象 填充,而不是副本对象的初始化为相同的值。
    【解决方案3】:

    有一种方法可以在 swift 中获得相当于 default(T) 的方法,但它不是免费的,并且有相关的危险:

    public func defaultValue<T>() -> T {
        let ptr = UnsafeMutablePointer<T>.alloc(1)
        let retval = ptr.memory
        ptr.dealloc(1)
        return retval;
    }
    

    现在这显然是一个 hack,因为我们不知道 alloc() 是否初始化为已知的东西。都是0吗?堆里剩下的东西?谁知道?此外,今天的情况明天可能会有所不同。

    事实上,对占位符以外的任何东西使用返回值是很危险的。假设您有这样的代码:

    public class Foo { /* implementation */
    public struct Bar { public var x:Foo }
    var t = defaultValue<Bar>();
    t = someFactoryThatReturnsBar(); // here's our problem
    

    在问题行,Swift 认为 t 已经初始化,因为这就是 Swift 的语义所说的:你不能有一个未初始化的值类型的变量。除了这是因为default&lt;T&gt; 破坏了这些语义。当您执行分配时,Swift 会向值见证表发出调用以销毁现有类型。这将包括将在字段x 上调用release 的代码,因为Swift 语义表明对象的实例永远不会是nil。然后你会遇到运行时崩溃。

    但是,我有理由与另一种语言的 Swift 进行互操作,我必须传入一个可选类型。不幸的是,由于某些原因(至少我还没有找到方法),Swift 没有为我提供一种在运行时构造可选的方法,而且我不能轻易地模拟一个,因为可选是根据通用枚举实现的并且枚举使用记录不充分的 5 策略实现来打包枚举的有效负载。

    我通过传递一个元组来解决这个问题,我将把它称为美杜莎元组,只是为了笑:(value: T, present: Bool),它的合同如果presenttrue,那么value 保证是有效,否则无效。我现在可以安全地使用它来进行互操作:

    public func toOptional<T>(optTuple: (value:T, present:Bool)) -> T? 
    {
        if optTuple.present { return optTuple.value }
        else { return nil }
    }
    
    public func fromOptional<T>(opt: T?) -> (T, Bool)
    {
        if opt != nil { return (opt!, true) }
        else {
            return (defaultValue(), false)
        }
    }
    

    这样,我的调用代码传入一个元组而不是一个可选项,接收代码并将其转换为可选项(反之亦然)。

    【讨论】:

      【解决方案4】:

      事实上,泛型类型可以有可选值。 您可以使用协议来做到这一点

      class ViewController: UIViewController {
      
         override func viewDidLoad() {
             super.viewDidLoad()
          
             let someType = SomeType<String>()
             let someTypeWithDefaultGeneric = SomeType()
         }
      
      }
      
      protocol Default {
         init()
      }
      
      extension Default where Self: SomeType<Void> {
        init() {
           self.init()
        }
      }
      
      class SomeType<T>: Default {
         required init() {}
      }
      

      【讨论】:

        猜你喜欢
        • 2020-05-02
        • 2023-03-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-12-02
        • 1970-01-01
        相关资源
        最近更新 更多