我发现自己经常被迫在Swift中使用var属性,即使属性只被分配一次。
下面是一个例子:我发现让几个类型共享一个init(...)的唯一方法是把init(...)放在一个协议扩展中。但是如果我这样做,结构体的属性必须在运行协议扩展中的init(...)主体之前被赋一个伪值,当它得到它的“真实的”值时。
下面的例子运行时没有错误。color 是一个let属性,但如何在Piece的init(...)中赋值?
protocol Piece {
var color: Character {set get} // "w" or "b"
init()
}
extension Piece {
init(color c: Character) {
self.init()
color = c
// Some complex logic here all Pieces share
}
}
struct Knight: Piece {
var color: Character = "_" // annoying dummy value
internal init(){}
}
// ... the other Piece types here ...
let knight = Knight(color: "w")
为了更清楚地说明这一点,我希望这是我想要的:(由于let color的原因,这不会编译。)
protocol Piece {
let color: Character {get} // "w" or "b"
}
extension Piece {
init(color c: Character) {
color = c
// Some complex logic here all Pieces share
}
}
struct Knight: Piece {
let color: Character
}
// ... the other Piece types here ...
let knight = Knight(color: "w")
编辑(找到答案后,请参阅以下内容):主题行问题的另一种表述方式:* 如何让几个结构类型共享初始化逻辑,同时允许只读属性let?*
第二次编辑明确说明第二个代码示例无法编译。
4条答案
按热度按时间k3fezbri1#
Get-only协议属性很酷,因为符合类型在如何定义相应属性方面有很大的灵活性,所需要的只是属性是可获取的。
因此,如果您使用get-only属性定义协议:
相容型别可以将
color
属性定义为储存变数、let常数或计算属性。存储变量:
计算属性:
让常数:
以上每一个都满足了协议强加的可获取属性要求。
关于初始化,回想一下swift会自动为结构体创建默认的初始化器,这些初始化器有对应于结构体的每个存储属性的参数。Swift可以为Queen和Knight创建一个初始化器,如下所示:
因此,如果你想让
color
成为let常量,并希望能够用初始化器对其进行配置,那么上述Piece
和Knight
的定义就足够了,你不需要做任何额外的工作。你可以这样示例化一个骑士:
r9f1avp52#
在发现Charles Srstka对How to use protocols for stucts to emulate classes inheritance的答案后,我构建了一个解决方案。它不是最漂亮的,但它确实允许几个结构体类型共享初始化逻辑,同时允许用let定义只读属性。
这是可行的:
7gs2gvoe3#
通过一些重构,您可以实现最少的代码重复。下面是我从不同Angular 考虑您的场景的解决方案:
1.首先,我注意到你只把“w”或“B”作为你的颜色属性值。因为你只有两个(或者说是最小的)输入变量,你可以通过使用与类型和泛型相关联的协议,使颜色成为类型定义本身的一部分,而不是把它作为一个属性。这样你就不必担心在初始化过程中设置属性。
您可以创建一个协议,即
PieceColor
,并为每种颜色创建一个新类型,即Black
、White
,并且您的Piece
协议可以有一个符合PieceColor
的关联类型:这种方法还提供了安全保证,因为您现在将用户输入限制为仅代码设计处理的值,而不是对用户输入添加额外的验证。此外,这还有助于您根据颜色组实现片段之间的特定关系,即只有相反的颜色才能杀死对方等。
1.现在是你问题的主要部分,你可以创建一个静态方法来初始化你的片段,并对它进行一些共享的复杂处理,而不是试图自定义初始化器:
dxxyhpgq4#
出于兴趣,我们采用了一种完全替代的方法:
首先我们定义一个咖喱函数--这个函数取自https://github.com/pointfreeco/swift-prelude:
现在让我们假设有一个棋子,它 * 有 * 一个角色,角色是棋子的类型。角色可以改变,因为你可以提升卒。这里我们用字符串来表示角色:
为了共享init,我们需要白色块和黑色块,我们咖喱init函数:
且产生两个部分应用init函数,其以W或B烘焙颜色:
现在,我们可以通过沿着剩余的参数来完成部分应用程序,以完全示例化一个棋子:
现在,使Role不是字符串,而是某种自定义类型(枚举),封装表示各种棋子之间差异的逻辑。
将
Piece.init(color:role:)
设为私有,公开curried版本。无需协议。