[英]How to achieve object merging through generics in swift?
Imagine I have a class Number: 想象一下,我有一个班级编号:
class Number {
var val: Double?
}
and have two instances of that class, A
and B
. 并且有两个类
A
和B
实例。 Now imagine I want to merge B
into A
through a statement like 现在假设我想通过类似的语句将
B
合并到A
merge(B, into: A)
Now of course I could write the function like this: 现在我当然可以写这样的函数:
func merge(from: Number, into: Number){
into.val = from.val
}
But that isn't reusable at all. 但这根本不可重复使用。 Is there a way I could write a generic merge class?
有没有办法可以编写泛型合并类?
UPDATE : Although some of the answers offer good and viable solutions, none of them are "generic" enough (generic here is meant in a non-technical way).So looking at the answers, I got some inspiration, and here is the solution I am now considering: make Number a NSObject subclass and declare all the properties that can be merged as dynamic. 更新 :虽然一些答案提供了良好和可行的解决方案,但它们都不够“通用”(这里的通用意味着非技术方式)。所以我看到了答案,我得到了一些灵感,这就是解决方案我现在正在考虑:使Number成为一个NSObject子类,并声明所有可以合并为动态的属性。 For example:
例如:
class Number: NSObject {
//Put the required init and initWithCoder: here
dynamic var val: Double?
}
Then declaring a protocol that mergeable classes must respect 然后声明可合并类必须遵守的协议
protocol Mergeable: class {
var mergeablePropertyKeys:[String] {get}
}
And then declaring a global function that performs a merge: 然后声明执行合并的全局函数:
func merge<U: Mergeable, Mergeable where U.Type == V.Type>(from: U, into:V){
for property in U.mergeablePropertyKeys {
V.setValue(U.valueForKey(property), property)
}
}
And I know that this will not work because the arguments to merge are not necessarily NSObjects
. 我知道这不会起作用,因为合并的参数不一定是
NSObjects
。
merge
are both NSObjects?
merge
的参数都是NSObjects?
Im not sure what you are expecting but there is generic solution: 我不确定你的期望,但有通用的解决方案:
class Number<T> {
var val: T?
}
protocol Merge {
func merge(from: Self, into: Self)
}
extension Number: Merge {
func merge(from: Number, into: Number) {
into.val = from.val
}
}
Lets define a HasValue
protocol (available only for classes) like this 让我们像这样定义一个
HasValue
协议(仅适用于类)
protocol HasValue: class {
typealias T
var val: T? { get set }
}
Now we can define a generic function 现在我们可以定义一个泛型函数
func merge<U: HasValue, V:HasValue where U.T == V.T>(from: U, into:V) {
into.val = from.val
}
The constraints in the function signature do guarantee that 函数签名中的约束可以保证这一点
HasValue
(therefore are classes) HasValue
(因此是类) val
types for both params are equals val
类型是等于的 class Number: HasValue {
var val: Double?
}
let one = Number()
one.val = 1
let two = Number()
two.val = 2
merge(one, into: two)
print(two.val) // Optional(1.0)
I did not constrain the 2 params of Merge
to having the same type, I am only checking that the val
properties of the 2 params must have the same type. 我并没有将
Merge
的2个参数限制为具有相同的类型,我只是检查2个参数的val
属性必须具有相同的类型。
So we could also merge different instances of different classes having val of the same type like 所以我们也可以合并具有相同类型的val的不同类的不同实例
class Phone: HasValue {
var val: Int?
}
class Computer: HasValue {
var val: Int?
}
let iPhone = Phone()
iPhone.val = 10
let iMac = Computer()
iMac.val = 9
merge(iPhone, into: iMac)
print(iMac.val) // Optional(10)
class Box<S>: HasValue {
var val: S?
}
let boxOfString = Box<String>()
boxOfString.val = "hello world"
let boxOfInt = Box<Int>()
boxOfInt.val = 12
merge(boxOfString, into: boxOfInt) // << compile error
let boxOfWords = Box<String>()
boxOfWords.val = "What a wonderful world"
merge(boxOfString, into: boxOfWords)
print(boxOfWords.val) // Optional("hello world")
It sounds like what you want is a generic function that uses reflection to merge properties. 听起来你想要的是一个使用反射来合并属性的泛型函数。 Reflection is limited in Swift, but it is doable using the MirrorType .
Swift中的反射是有限的,但使用MirrorType是可行的。 I have used this method before to build a generic json parser in swift - you could do something similar but instead of parsing a json dictionary to properties map your two object's properties.
我之前使用过这个方法在swift中构建一个通用的json解析器 - 你可以做类似的事情但不是解析json字典到属性映射你的两个对象的属性。
An example of using reflection to do this in swift: 使用反射在swift中执行此操作的示例:
func merge<T>(itemToMerge:T) {
let mirrorSelf = Mirror(reflecting: self)
let mirrorItemToMerge = Mirror(reflecting: itemToMerge)
for mirrorSelfItem in mirrorSelf.children {
// Loop through items in mirrorItemToMerge.
for mirrorImageItem in mirrorItemToMerge.children {
// If you have a parameter who's name is a match, map the value
// OR You could add any custom mapping logic you need for your specific use case
if mirrorSelfItem.label == mirrorImageItem.label {
// To set values, use self.setValue(valueToSet, forKey: propertyName)
self.setValue(mirrorImageItem.value as? AnyObject, forKey: mirrorImageItem.label!)
}
}
}
}
This assumes the object defining the merge method is a subclass of NSObject (so it can take advantage of NSKeyValueCoding). 这假设定义merge方法的对象是NSObject的子类(因此它可以利用NSKeyValueCoding)。 You could also make this a static method that could merge any 2 objects of any NSObject type:
你也可以使这个静态方法可以合并任何NSObject类型的任何2个对象:
static func merge<T1: NSObject, T2: NSObject>(itemChanging:T1, itemToMerge:T2) {
let mirrorSelf = Mirror(reflecting: itemChanging)
let mirrorItemToMerge = Mirror(reflecting: itemToMerge)
for mirrorSelfItem in mirrorSelf.children {
// Loop through items in mirrorItemToMerge.
for mirrorImageItem in mirrorItemToMerge.children {
// If you have a parameter who's name is a match, map the value
// OR You could add any custom mapping logic you need for your specific use case
if mirrorSelfItem.label == mirrorImageItem.label {
// To set values, use self.setValue(valueToSet, forKey: propertyName)
self.setValue(mirrorImageItem.value as? AnyObject, forKey: mirrorImageItem.label!)
}
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.