你能利用 Swift 的默认结构初始化器来简化参数传递吗?

Posted

技术标签:

【中文标题】你能利用 Swift 的默认结构初始化器来简化参数传递吗?【英文标题】:Can you leverage Swift's default initializers for structs to simplify parameter passing? 【发布时间】:2018-06-01 08:52:11 【问题描述】:

我正在设计我们的 API 以将具体查询对象作为单个参数,而不是展开所有可以传递参数的地方的签名。

例如,与其写这个...

func someQuery(requiredA:Bool, requiredB:Int, requiredC:String, defaultedA:Bool = false, defaultedB:Int = 1, defaultedC:String = "X", optionalA:Bool? = nil, optionalB:Int? = nil, optionalC:String? = nil)
    ....

我正在写这个……

func someQuery(queryParams:QueryParams)
    ....

我正在定义(部分--更多如下)这样的查询对象...;

struct QueryObject
    let requiredA  : Bool
    let requiredB  : Int
    let requiredC  : String
    let defaultedA : Bool
    let defaultedB : Int
    let defaultedC : String
    let optionalA  : Bool?
    let optionalB  : Int?
    let optionalC  : String?

我相信您可以从名称中看出,对于这个例子,有些是必需的,有些是默认的,有些是完全可选的。

现在至于定义 QueryParams 对象,我正在尝试这样做,因此当他们创建它时,用户只需要指定所需的参数,并且可以根据需要/认为合适来选择指定其他参数。

这就是我所追求的......

// At a minimum
let queryParams = QueryParams(requiredA:true, requiredB:1, requiredC:"x")

// Minimums overriding a default
let queryParams = QueryParams(requiredA:true, requiredB:1, requiredC:"x", defaultC:"y")

// Minimums plus an optional, still picking up all defaults
let queryParams = QueryParams(requiredA:true, requiredB:1, requiredC:"x", optionalB:8)

为了实现这一点,我创建了自己的初始化程序,它什么都不做,只是在内部分配参数,就像这样......

struct QueryParams

    init(
        requiredA  : Bool,
        requiredB  : Int,
        requiredC  : String,
        defaultedA : Bool    = true,
        defaultedB : Int     = 1,
        defaultedC : String  = "x",
        optionalA  : Bool?   = nil,
        optionalB  : Int?    = nil,
        optionalC  : String? = nil
    )
        self.requiredA  = requiredA
        self.requiredB  = requiredB
        self.requiredC  = requiredC
        self.defaultedA = defaultedA
        self.defaultedB = defaultedB
        self.defaultedC = defaultedC
        self.optionalA  = optionalA
        self.optionalB  = optionalB
        self.optionalC  = optionalC
    

    let requiredA  : Bool
    let requiredB  : Int
    let requiredC  : String
    let defaultedA : Bool
    let defaultedB : Int
    let defaultedC : String
    let optionalA  : Bool?
    let optionalB  : Int?
    let optionalC  : String?

...这似乎是很多样板代码。我正在尝试找出是否有任何方法可以利用 Swift 创建结构的默认初始化程序,这样我就不必手动编写所有这些了。

注意:

    我在这里选择结构是因为 Swift 为它们提供了默认的初始化器,而类 AFAIK 没有。 显然,必需的参数必须放在第一位。除此之外,没关系(尽管它们也必须按定义顺序排列也没关系。重点是我不在乎。) 成员变量可以是letvar。我只是习惯使用let

那么...可以简化/减少/消除这段代码吗?

【问题讨论】:

已编辑。随意对这样的事情做同样的事情。我就是这么做的。 【参考方案1】:

您无法单独从默认初始化程序中获得这种控制。就最少的代码而言,我会推荐这样的东西。

struct QueryParams

    let requiredA  : Bool
    let requiredB  : Int
    let requiredC  : String
    var defaultedA : Bool = true
    var defaultedB : Int = 1
    var defaultedC : String = "x"
    var optionalA  : Bool? = nil
    var optionalB  : Int? = nil
    var optionalC  : String? = nil


extension QueryParams 
    init(
        requiredA  : Bool,
        requiredB  : Int,
        requiredC  : String
        )
        self.requiredA  = requiredA
        self.requiredB  = requiredB
        self.requiredC  = requiredC
    

通过将自定义初始化程序放在扩展中,您可以免费保留默认初始化程序。唯一的问题是,如果他们只想自定义一些不需要的属性,他们需要在初始化后进行。

【讨论】:

是的,但是如果它强制您指定所有内容,那么保留默认初始化程序有什么意义呢?而且,如果您无论如何都必须编写一个初始化程序,那么您不妨充实它以使用默认值处理所有情况,如果您这样做,那么无论如何都不需要默认初始化程序。另外,一个完整的初始化器的另一个好处是所有东西都可以变成一个 Let。我只是希望有某种方法可以自动执行此操作,但我开始认为没有。 您的积分有效。在我看来,即使它看起来像很多样板,你也应该继续你正在做的事情。这只是一种根据您正在寻找的内容以最少的代码获得最大灵活性的方法。不幸的是,您正在寻找的东西不可用。太糟糕了,它无法检测到默认值并相应地调整默认初始化器。 完全同意。总有 Swift 5! :) 再次感谢!

以上是关于你能利用 Swift 的默认结构初始化器来简化参数传递吗?的主要内容,如果未能解决你的问题,请参考以下文章

在 Swift 中使用对象初始化器来替换 AllocWithZone

结构体构造函数使用总结

默认情况下,如何将成员初始化程序公开,用于 Swift 中的结构?

Swift开发注意点

Swift结构体(Struct)

在 Swift 中初始化 C 结构体