我正在迅速处理协议。我以为它将类似于其他语言中的“接口”。我正在测试它将如何与变量一起使用。协议对我来说是很新的,因为我从未见过带有非静态变量的接口。我创建了一个站协议。
protocol Station{
var id:String {get set}
var name:String {get set} // station name
var lines: Array<String> {get set} // all lines persent in this station
}
然后包含该站参考的行。它还包括可哈希协议。
protocol Line: Hashable {
var lineId: String{get set}
var station1:Station {get set} // start station
var station2:Station {get set} // end station
var stations:Array<Station> {get set} // all the stations that is in this line
}
这是该协议的实现
struct LinesImpl: Line{
var station1: Station
var station2: Station
var stations: Array<Station>
var lineId: String
static func == (lhs: LinesImpl, rhs: LinesImpl) -> Bool {
return (lhs.station1.name == rhs.station1.name && lhs.station2.name == rhs.station2.name && lhs.lineId == rhs.lineId)
}
func hash(into hasher: inout Hasher) {
hasher.combine(station1.name)
hasher.combine(station2.name)
hasher.combine(lineId)
}
init(station1: Station, station2: Station, stations: [Station], lineId: String){
self.station1 = station1
self.station2 = station2
self.stations = stations
self.lineId = lineId
}
}
一旦我添加了Hashable。我不再能够为Line协议创建引用。
当我尝试这样做时,它说“协议'Line'只能用作通用约束,因为它具有Self或关联的类型要求”:
我在这里看到了很多类似的问题,但是我仍然看不到错误是什么。任何帮助都是极好的。谢谢你们。
答案 0 :(得分:4)
添加Hashable
时,添加了Equatable
,并且由于Equatable
是PAT,因此使它成为具有关联类型(PAT)的协议。 PAT不是一种类型。这是用于将方法添加到其他类型的工具。
不能将PAT用作变量的类型,不能将其放入数组中,不能直接将其作为参数传递,也不能将其作为值返回。 PAT的唯一要点是作为通用约束(where L: Line
)。 PAT指出了另一个具体类型必须提供的内容,以便在某些情况下可用。
该如何解决尚不清楚。这看起来似乎根本不是协议。这取决于您要在此处解决的代码重用问题。
协议通常是关于可以做什么的内容。 Line
似乎只是在试图隐藏实现,而不表达任何内容。那不是协议。如所写,这里根本没有理由使用泛型或协议。 Line
的其他实现是什么样的? (我很难想象您还能如何实现这种类型。)
我怀疑正确的答案是将所有这些替换为Station
结构和Line
结构。我没有看到协议在哪里发挥作用。
这是一种我可以实现您正在构建的内容的方法,以及一些新协议以查看它们的用途。其中一些可能比您需要解决的更多,但我想展示实际的协议。
// A protocol for "ID" types that automatically gives them handy inits
// Nested ID types mean that Line.ID can't get confused with Station.ID.
// The point of a protocol is to add additional features to a type like this.
protocol IDType: Hashable, ExpressibleByStringLiteral, CustomStringConvertible {
var value: String { get }
init(value: String)
}
extension IDType {
// For convenience
init(_ value: String) { self.init(value: value) }
// Default impl for ExpressibleByStringLiteral
init(stringLiteral value: String) { self.init(value: value) }
// Default impl for CustomStringConvertible
var description: String { return value }
}
struct Line: Equatable {
struct ID: IDType { let value: String }
let id: ID
let stations: [Station]
var origin: Station { return stations.first! } // We ensure during init that stations is non-empty
var terminus: Station { return stations.last! }
init(id: ID, origin: Station, stops: [Station], terminus: Station) {
self.id = id
self.stations = [origin] + stops + [terminus]
}
}
// Conforming Line to this protocol lets it print more beautifully.
extension Line: CustomStringConvertible {
var description: String { return "\(id): \(origin) -> \(terminus)" }
}
// Stations can't contain Line directly. These are value types, and that would be
// recursive. But this is nice because it lets us construct immutable Stations
// and then glue them together with Lines which may even be in independent
// systems (for example, the bus system might be separate from the rail system,
// but share stations)
struct Station: Hashable {
struct ID: IDType { let value: String }
let id: ID
let name: String
func lines(in system: System) -> [Line] {
return system.linesVisiting(station: self)
}
}
extension Station: CustomStringConvertible {
var description: String { return name }
}
struct System: Equatable {
let lines: [Line]
// Using Set here makes it clear there are no duplicates, and saves
// some hassle turning it back into an Array, but we could also just
// return Array here as Array(Set(...))
var stations: Set<Station> {
// Uniquify the stations
return Set(lines.flatMap { $0.stations })
}
func linesVisiting(station: Station) -> [Line] {
return lines.filter { $0.stations.contains(station) }
}
}
// Some examples of using it.
let stationNames = ["Shady Grove", "Bethesda", "Metro Center", "Glenmont",
"Wiehle-Reston East", "Largo Town Center"]
// Build up a few stations; obviously there are many more
let stations = Dictionary(uniqueKeysWithValues:
stationNames.map { ($0, Station(id: .init($0), name: $0)) })
// Define some lines
let redLine = Line(id: "OR",
origin: stations["Shady Grove"]!,
stops: [stations["Bethesda"]!, stations["Metro Center"]!],
terminus: stations["Glenmont"]!)
let silverLine = Line(id: "SV",
origin: stations["Wiehle-Reston East"]!,
stops: [stations["Metro Center"]!],
terminus: stations["Largo Town Center"]!)
// And glue them together into a system
let system = System(lines: [redLine, silverLine])