Ich beschloss, ein besseres Verständnis der Typ Löschung zu erhalten, indem Sie einigen einfachen Code zu schreiben. Ich habe ein generisches Soldier-Protokoll. Soldaten haben Waffen und Soldaten können kämpfen. Ich würde gerne eine Armee von verschiedenen Arten von Soldaten schaffen. Ich dachte, diese Art der Auslöschung würde mir die Möglichkeit geben, die Soldier Adopters so zu verpacken, dass ich sie als einfache Soldaten behandeln könnte (anstelle von Snipern, Infanteristen usw.). Aber ich fand, dass der intermediäre, boxende Typ (der Typ-Radierer) muss immer noch generisch über Soldier's assoziierten Typ (dh Waffe) gemacht werden. Also kann ich Gewehr mit Soldaten oder Raketen mit Soldaten, aber nicht nur einfache Soldaten haben. Gibt es etwas über die Verwendung von Typ löschen, die ich verpasst habe?Type Erasure: Habe ich etwas verpasst?
import Foundation
// Soldiers have weapons and soldiers can fight
protocol Weapon {
func fire()
}
protocol Soldier {
associatedtype W: Weapon
var weapon: W { get }
func fight()
}
extension Soldier {
func fight() { weapon.fire() }
}
// Here are some weapons
struct Rifle : Weapon {
func fire() { print("Bullets away!") }
}
struct Rocket : Weapon {
func fire() { print("Rockets away!") }
}
struct GrenadeLauncher : Weapon {
func fire() { print("Grernades away!") }
}
// Here are some soldiers
struct Sniper : Soldier {
var weapon = Rifle()
}
struct Infantryman : Soldier {
var weapon = Rifle()
}
struct Artillaryman : Soldier {
var weapon = Rocket()
}
struct Grenadier : Soldier {
var weapon = GrenadeLauncher()
}
// Now I would like to have an army of soldiers but the compiler will not let me.
// error: protocol 'Soldier' can only be used as a generic constraint because it has Self or associated type requirements
class Army {
var soldiers = [Soldier]()
func join(soldier: Soldier) {
soldiers.append(soldier)
}
func makeWar() {
for soldier in soldiers { soldier.fight() }
}
}
// So, let's try the type erasure technique:
struct AnySoldier<W: Weapon> : Soldier {
var weapon: W
private let _fight:() -> Void
init<S: Soldier>(soldier: S) where S.W == W {
_fight = soldier.fight
weapon = soldier.weapon
}
func fight() { _fight() }
}
var s1 = AnySoldier(soldier: Sniper())
print (type(of: s1)) // AnySoldier<Rifle>
s1.fight() // Bullets away!
s1.weapon.fire() // Bullets away!
s1 = AnySoldier(soldier: Infantryman()) // Still good; Infantrymen use rifles
s1 = AnySoldier(soldier: Grenadier()) // Kaboom! Grenadiers do not use rifles
// So now I can have an army of Rifle wielding Soldiers
class Army {
var soldiers = [AnySoldier<Rifle>]()
func join(soldier: AnySoldier<Rifle>) {
soldiers.append(soldier)
}
func makeWar() {
for soldier in soldiers { soldier.fight() }
}
}
let army = Army()
army.join(soldier: AnySoldier(soldier: Sniper()))
army.join(soldier: AnySoldier(soldier: Infantryman()))
army.join(soldier: AnySoldier(soldier: Grenadier())) // Kaboom! Rifles only
army.makeWar()
// But, what I really want is an army wherein the weapons are unrestricted.
class Army {
var soldiers = [AnySoldier]()
func join(soldier: AnySoldier) {
soldiers.append(soldier)
}
func makeWar() {
for soldier in soldiers { soldier.fight() }
}
}
Das war es, Rob. Vielen Dank. Übrigens: Ich liebe deine Gespräche. Ich werde Beyond Crusty wiedersehen - sobald ich mir Zeit nehme, das Original zu sehen. – Verticon