如何跨具有冲突 属性 名称的结构实现 Swift 协议
How to implement a Swift protocol across structs with conflicting property names
我正在尝试编写一个协议来协调描述相同概念的两个不同结构,某种停止。两者都有 Code
、Description
和 Latitude
& Longitude
坐标,但对于一种类型,Description
可能是 nil
,对于另一种类型,坐标可能是 nil
。
如何编写一个协议来协调这两个结构?
这是我的协议:
protocol Stop {
var Code : String { get }
var Description : String { get }
var Latitude : Double { get }
var Longitude : Double { get }
}
以及两种类型的止损:
struct BusStop : Stop { // Compiler error: doesn't implement Description
var Code : String
var Description : String?
var Latitude : Double
var Longitude : Double
// Various other properties
}
struct TrainStop : Stop { // Compiler error: doesn't implement Latitude or Longitude
var Code : String
var Description : String
var Latitude : Double?
var Longitude : Double?
// Various other properties
}
在 C#(我的母语)中,我会像这样编写一个显式接口实现(伪代码):
// At the end of the BusStop struct
var Stop.Description : String { return Description ?? string.Empty }
// At the end of the TrainStop struct
var Stop.Latitude : Double { return Latitude ?? 0 }
var Stop.Longitude : Double { return Longitude ?? 0 }
但是,我不知道 Swift 中有任何类似的功能。鉴于我无法更改 BusStop
和 TrainStop
的现有 属性 定义,我该如何编写 Stop
协议,以便它环绕结构和 returns 属性何时可用?
显式接口实现的理想特性是它们是静态分派的,对吧?如果你在 BusStop
上使用 Description
,它将是一个可选字符串,但如果你在 Stop
上使用 Description
,它将是一个非可选字符串。
在Swift中,扩展成员是静态调度的,所以你可以利用它来实现类似的东西:
extension Stop where Self == BusStop {
// Since the type of "self" here is BusStop, "Description" refers to the one declared in BusStop
// not this one here, so this won't cause infinite recursion
var Description : String { return self.Description ?? "" }
}
extension Stop where Self == TrainStop {
var Latitude: Double { return self.Latitude ?? 0 }
var Longitude: Double { return self.Longitude ?? 0 }
}
这段代码表明这是可行的:
let busStop = BusStop(Code: "ABC", Description: "My Bus Stop", Latitude: 0, Longitude: 0)
print(type(of: busStop.Description)) // Optional<String>
let stop: Stop = busStop
print(type(of: stop.Description)) // String
但是,我仍然不认为这是好的 Swift 代码。直接将 API 从一种语言翻译成另一种语言通常是不好的。如果我是你,我会把 Longitude
、Latitude
和 Description
in Stop
全部设为可选项。
我同意@Sweeper 的观点,如果您发现需要将不同的数据布局置于同一保护伞下,那么您的设计可能不是最好的。尽管如此,我想分享另一种可能的方法来解决您的问题。
不是将所有 Stop
属性都放入一个协议中,而是可以将它们推入另一个结构中,并让 Stop
协议 return 结构为:
protocol Stop {
var stopData: StopData { get }
}
struct StopData {
var code: String
var stopDescription: String
var latitude: Double
var longitude: Double
}
然后您可以为您的两个结构添加以下一致性:
struct BusStop: Stop {
var code: String
var busStopDescription: String?
var latitude: Double
var longitude: Double
var stopData: StopData {
return StopData(code: code,
stopDescription: busStopDescription ?? "",
latitude: latitude,
longitude: longitude)
}
}
struct TrainStop: Stop {
var code: String
var trainStopDescription: String
var latitude: Double?
var longitude: Double?
var stopData: StopData {
return StopData(code: code,
stopDescription: trainStopDescription,
latitude: latitude ?? 0,
longitude: longitude ?? 0)
}
}
这意味着您将在应用程序的其余部分而不是 Stop
协议中循环 StopData
个实例。
P.S。我还更改了 属性 名称,使其更符合 Swift 命名准则。
我正在尝试编写一个协议来协调描述相同概念的两个不同结构,某种停止。两者都有 Code
、Description
和 Latitude
& Longitude
坐标,但对于一种类型,Description
可能是 nil
,对于另一种类型,坐标可能是 nil
。
如何编写一个协议来协调这两个结构?
这是我的协议:
protocol Stop {
var Code : String { get }
var Description : String { get }
var Latitude : Double { get }
var Longitude : Double { get }
}
以及两种类型的止损:
struct BusStop : Stop { // Compiler error: doesn't implement Description
var Code : String
var Description : String?
var Latitude : Double
var Longitude : Double
// Various other properties
}
struct TrainStop : Stop { // Compiler error: doesn't implement Latitude or Longitude
var Code : String
var Description : String
var Latitude : Double?
var Longitude : Double?
// Various other properties
}
在 C#(我的母语)中,我会像这样编写一个显式接口实现(伪代码):
// At the end of the BusStop struct
var Stop.Description : String { return Description ?? string.Empty }
// At the end of the TrainStop struct
var Stop.Latitude : Double { return Latitude ?? 0 }
var Stop.Longitude : Double { return Longitude ?? 0 }
但是,我不知道 Swift 中有任何类似的功能。鉴于我无法更改 BusStop
和 TrainStop
的现有 属性 定义,我该如何编写 Stop
协议,以便它环绕结构和 returns 属性何时可用?
显式接口实现的理想特性是它们是静态分派的,对吧?如果你在 BusStop
上使用 Description
,它将是一个可选字符串,但如果你在 Stop
上使用 Description
,它将是一个非可选字符串。
在Swift中,扩展成员是静态调度的,所以你可以利用它来实现类似的东西:
extension Stop where Self == BusStop {
// Since the type of "self" here is BusStop, "Description" refers to the one declared in BusStop
// not this one here, so this won't cause infinite recursion
var Description : String { return self.Description ?? "" }
}
extension Stop where Self == TrainStop {
var Latitude: Double { return self.Latitude ?? 0 }
var Longitude: Double { return self.Longitude ?? 0 }
}
这段代码表明这是可行的:
let busStop = BusStop(Code: "ABC", Description: "My Bus Stop", Latitude: 0, Longitude: 0)
print(type(of: busStop.Description)) // Optional<String>
let stop: Stop = busStop
print(type(of: stop.Description)) // String
但是,我仍然不认为这是好的 Swift 代码。直接将 API 从一种语言翻译成另一种语言通常是不好的。如果我是你,我会把 Longitude
、Latitude
和 Description
in Stop
全部设为可选项。
我同意@Sweeper 的观点,如果您发现需要将不同的数据布局置于同一保护伞下,那么您的设计可能不是最好的。尽管如此,我想分享另一种可能的方法来解决您的问题。
不是将所有 Stop
属性都放入一个协议中,而是可以将它们推入另一个结构中,并让 Stop
协议 return 结构为:
protocol Stop {
var stopData: StopData { get }
}
struct StopData {
var code: String
var stopDescription: String
var latitude: Double
var longitude: Double
}
然后您可以为您的两个结构添加以下一致性:
struct BusStop: Stop {
var code: String
var busStopDescription: String?
var latitude: Double
var longitude: Double
var stopData: StopData {
return StopData(code: code,
stopDescription: busStopDescription ?? "",
latitude: latitude,
longitude: longitude)
}
}
struct TrainStop: Stop {
var code: String
var trainStopDescription: String
var latitude: Double?
var longitude: Double?
var stopData: StopData {
return StopData(code: code,
stopDescription: trainStopDescription,
latitude: latitude ?? 0,
longitude: longitude ?? 0)
}
}
这意味着您将在应用程序的其余部分而不是 Stop
协议中循环 StopData
个实例。
P.S。我还更改了 属性 名称,使其更符合 Swift 命名准则。