如何使用来自内部协议的初始值设定项在 public 函数中构造新值?
How to construct a new value in a public function using initializers from an internal protocol?
假设我想公开一些功能 public API,但部分实现涉及内部协议:
public protocol P {
var foo: Int { get }
}
internal protocol Q {
init(from: [String])
}
public struct S: P, Q {
public var foo: Int = 0
public init() {}
internal init(from: [String]) {
precondition(from.count > 0)
self.foo = Int(from[0])!
}
}
这将是一些数据对象,只有我自己的模块可以构造(从一些数据表示)但是模块的用户可以出于他们自己的目的使用。
假设我想提供一些具有这样值的服务和returns一个相同类型的新服务:
public class ProviderOfThings {
public func map<T: P>(before: T) -> T {
return T(from: [String(before.foo + 1)])
}
}
这不编译; T
.
上没有合适的初始值设定项
如何以通用方式调用我(内部)知道的构造函数?
我们可以将值转换为内部协议类型并从那里访问初始化器:
public static func map<T: P>(before: T) -> T {
return type(of: before as! Q).init(
from: [String(before.foo + 1)]
) as! T
}
我们甚至可以不用类型的值(感谢,Hamish!):
public static func make<T: P>() -> T {
return (T.self as! Q.Type).init(
from: ["0"]
) as! T
}
如果 P
有(或可能有)不符合 Q
的实现,则需要注意对 Q
的强制转换,显然:
guard let beforeQ = before as? Q else { ... }
// or
guard before is Q else { ... }
// respectively
guard T.self is Q.Type else { ... }
另一个转换 as! T
是安全的:毕竟我们调用了 T
的初始值设定项。因此,如果有所防范,该解决方案不会导致运行时错误。
查找带有示例的完整代码 here。
我认为最好的方法是声明 map
如下:
public func map<T: P & Q>(before: T) -> T
并生成 Q
public,如果它不应该被外部代码使用,可能会在它前面加上 _
前缀。
假设我想公开一些功能 public API,但部分实现涉及内部协议:
public protocol P {
var foo: Int { get }
}
internal protocol Q {
init(from: [String])
}
public struct S: P, Q {
public var foo: Int = 0
public init() {}
internal init(from: [String]) {
precondition(from.count > 0)
self.foo = Int(from[0])!
}
}
这将是一些数据对象,只有我自己的模块可以构造(从一些数据表示)但是模块的用户可以出于他们自己的目的使用。
假设我想提供一些具有这样值的服务和returns一个相同类型的新服务:
public class ProviderOfThings {
public func map<T: P>(before: T) -> T {
return T(from: [String(before.foo + 1)])
}
}
这不编译; T
.
如何以通用方式调用我(内部)知道的构造函数?
我们可以将值转换为内部协议类型并从那里访问初始化器:
public static func map<T: P>(before: T) -> T {
return type(of: before as! Q).init(
from: [String(before.foo + 1)]
) as! T
}
我们甚至可以不用类型的值(感谢,Hamish!):
public static func make<T: P>() -> T {
return (T.self as! Q.Type).init(
from: ["0"]
) as! T
}
如果 P
有(或可能有)不符合 Q
的实现,则需要注意对 Q
的强制转换,显然:
guard let beforeQ = before as? Q else { ... }
// or
guard before is Q else { ... }
// respectively
guard T.self is Q.Type else { ... }
另一个转换 as! T
是安全的:毕竟我们调用了 T
的初始值设定项。因此,如果有所防范,该解决方案不会导致运行时错误。
查找带有示例的完整代码 here。
我认为最好的方法是声明 map
如下:
public func map<T: P & Q>(before: T) -> T
并生成 Q
public,如果它不应该被外部代码使用,可能会在它前面加上 _
前缀。