我有以下代码:
protocol Protocol_1 {
var prop_1: String { get set }
var prop_2: Int { get set }
}
protocol Protocol_2 {
var prop_3: Double { get set }
var prop_4: Bool { get set }
}
extension Protocol_1 {
mutating func set<T>(value: T, forKey key: String)
{
switch key {
case "prop_1":
if value is String {
prop_1 = value as! String
}
case "prop_2":
if value is Int {
prop_2 = value as! Int
}
default:
break
}
}
}
extension Protocol_2 {
mutating func set<T>(value: T, forKey key: String)
{
switch key {
case "prop_3":
if value is Double {
prop_3 = value as! Double
}
case "prop_4":
if value is Bool {
prop_4 = value as! Bool
}
default:
break
}
}
}
struct MyStruct : Protocol_1, Protocol_2 {
var prop_1: String
var prop_2: Int
var prop_3: Double
var prop_4: Bool
}
var myStruct = MyStruct(prop_1: "hello", prop_2: 0, prop_3: 3.5, prop_4: true)
myStruct.set(value: "bye", forKey: "prop_1")
现在游乐场给我一个错误,因为不清楚应该调用什么 set 函数。 游乐场执行失败:错误:ProtocolsPlayground.playground:59:1:错误:模糊使用&#39; set(value:forKey :)&#39; myStruct.set(值:&#34;再见&#34;,forKey:&#34; prop_1&#34;)
这很清楚,但我怎样才能实现这样的功能扩展或是否有解决方法?特别是如果 Protocol_1 不可编辑。
答案 0 :(得分:2)
我不知道如何使用协议扩展现有功能或其他协议的功能。
我建议您将使用第三个协议分配属性的机制与两个协议分开。
这是解决这个问题的一种方法:
定义一个类,它将根据名称(键)和变量引用之间的映射来处理获取和设置属性:
class PropertyMapper
{
static var sharedGetter = PropertyMapperGet()
static var sharedSetter = PropertyMapperSet()
var value : Any = 0
var success = false
var key = ""
func map<T>(_ key:String, _ variable:inout T) {}
func clear()
{
value = 0
success = false
key = ""
}
}
class PropertyMapperGet:PropertyMapper
{
func get(forKey:String)
{
key = forKey
success = false
}
override func map<T>(_ key:String, _ variable:inout T)
{
if !success && self.key == key
{
value = variable
success = true
}
}
}
class PropertyMapperSet:PropertyMapper
{
override func map<T>(_ key:String, _ variable:inout T)
{
if !success && self.key == key,
let newValue = value as? T
{
variable = newValue
success = true
}
}
func set(value newValue:Any, forKey:String)
{
key = forKey
value = newValue
success = false
}
}
然后,您可以为所有能够按名称(键)分配其属性的结构和类定义协议:
protocol MappedProperties
{
mutating func mapProperties(_ :PropertyMapper)
}
extension MappedProperties
{
mutating func get(_ key:String) -> Any?
{
let mapper = PropertyMapper.sharedGetter
defer { mapper.clear() }
mapper.get(forKey:key)
mapProperties(mapper)
return mapper.success ? mapper.value : nil
}
@discardableResult
mutating func set(value:Any, forKey key:String) -> Bool
{
let mapper = PropertyMapper.sharedSetter
defer { mapper.clear() }
mapper.set(value:value, forKey:key)
mapProperties(mapper)
return mapper.success
}
}
您的协议可能要求采用它们的结构提供命名分配。 (进一步了解如何使属性映射成为协议的一部分)
protocol Protocol_1:MappedProperties
{
var prop_1: String { get set }
var prop_2: Int { get set }
}
protocol Protocol_2:MappedProperties
{
var prop_3: Double { get set }
var prop_4: Bool { get set }
}
您的结构需要实现属性映射才能采用您的协议。键/变量映射在整个结构的集中函数中执行,需要为两个协议中的所有变量提供密钥。
struct MyStruct : Protocol_1, Protocol_2
{
var prop_1: String
var prop_2: Int
var prop_3: Double
var prop_4: Bool
mutating func mapProperties(_ mapper:PropertyMapper)
{
mapper.map("prop_1", &prop_1)
mapper.map("prop_2", &prop_2)
mapper.map("prop_3", &prop_3)
mapper.map("prop_4", &prop_4)
}
}
这允许struct按名称(键)分配属性:
var myStruct = MyStruct(prop_1: "hello", prop_2: 0, prop_3: 3.5, prop_4: true)
myStruct.set(value: "bye", forKey: "prop_1")
为了进一步优化并使属性映射成为协议的一部分,您可以向协议添加映射函数,以便采用它的结构不必知道此映射的详细信息。
他们仍然需要实现映射协议的功能,但他们可以简单地使用协议提供的功能来完成这项工作。
例如(我只显示了Protocol_1,但您可以同时使用它们):
extension Protocol_1
{
mutating func mapProtocol_1(_ mapper:PropertyMapper)
{
mapper.map("prop_1", &prop_1)
mapper.map("prop_2", &prop_2)
}
}
使用协议提供的功能,结构不需要知道映射了哪些属性。这应该使结构和协议的维护不易出错,并避免重复。
struct MyStruct : Protocol_1, Protocol_2
{
var prop_1: String
var prop_2: Int
var prop_3: Double
var prop_4: Bool
mutating func mapProperties(_ mapper:PropertyMapper)
{
mapProtocol_1(mapper)
mapper.map("prop_3", &prop_3)
mapper.map("prop_4", &prop_4)
}
}