Swift 为什么可以';泛型结构类型不能由它初始化时使用的参数推断出来吗?
我不确定我是否能足够清楚地描述这个问题,但我已经成功地制作了一个小的(足够)可重复的例子。在这段代码中,我为源a中的一个实体和源B中的一个实体创建了一个带有协议约束扩展的通用结构。调用了正确扩展中的方法Swift 为什么可以';泛型结构类型不能由它初始化时使用的参数推断出来吗?,swift,generics,Swift,Generics,我不确定我是否能足够清楚地描述这个问题,但我已经成功地制作了一个小的(足够)可重复的例子。在这段代码中,我为源a中的一个实体和源B中的一个实体创建了一个带有协议约束扩展的通用结构。调用了正确扩展中的方法 protocol Entity { } protocol FromSource_A: Entity { } protocol FromSource_B: Entity { } struct Apple: FromSource_A { } struct Orange: FromSource_B
protocol Entity { }
protocol FromSource_A: Entity { }
protocol FromSource_B: Entity { }
struct Apple: FromSource_A { }
struct Orange: FromSource_B { }
protocol StructProtocol {
func go ()
}
struct MyStruct<T: Entity>: StructProtocol {
func go () {
print("MyStruct default go()")
}
}
extension MyStruct where T : FromSource_A {
func go () {
print("MyStruct extension where T : FromSource_A")
}
}
extension MyStruct where T : FromSource_B {
func go () {
print("MyStruct extension where T : FromSource_B")
}
}
let myStruct = MyStruct<Apple>()
myStruct.go() // <- Output: "MyStruct extension where T : FromSource_A"
而是调用MyStruct
的默认方法实现
protocol Entity { }
protocol FromSource_A: Entity { }
protocol FromSource_B: Entity { }
struct Apple: FromSource_A { }
struct Orange: FromSource_B { }
protocol StructProtocol {
func go ()
}
struct MyStruct<T: Entity>: StructProtocol {
func go () {
print("MyStruct default go()")
}
}
extension MyStruct where T : FromSource_A {
func go () {
print("MyStruct extension where T : FromSource_A")
}
}
extension MyStruct where T : FromSource_B {
func go () {
print("MyStruct extension where T : FromSource_B")
}
}
let myStruct = MyStruct<Apple>()
myStruct.go() // <- Output: "MyStruct extension where T : FromSource_A"
就我所见,当我引入另一个层来传递泛型信息(func-test(argument:genericalargument){}
)时,问题就出现了。在这个层中,给定了一个具体的参数,接收者无法确定它的泛型类型已经填充了什么
当方法的类型由初始化时使用的参数推断时,为什么不在正确的扩展名中调用该方法?您似乎在尝试从泛型创建类继承。那是不可能的。泛型不是动态调度的。这是故意的,允许进行更多的优化 使用
where
子句在默认实现上提供专门的扩展,就像您在这里所做的那样,只应该用于性能改进。如果编译器能够证明关于类型的某些东西(例如,它是一个双向集合而不是序列),那么提供一个更有效的算法来生成相同的输出可能会很有用。但是对MyStruct.go()
的所有调用都应该具有相同的语义(产生相同的输出)。关于调用哪个版本的go
的决定是在编译时根据编译时可用的信息做出的。test()
可能会从程序的其他部分以不同的类型调用,因此该函数无法专用于应用正确的where
子句。它必须假设允许的最一般情况
在这种特定情况下,如果我添加以下行,您希望发生什么情况:
extension Apple: FromSource_B {}
这是完全合法的,因为苹果符合源代码。我甚至可以在另一个模块中添加这行代码(在这里的所有内容都编译好之后)。那么应该运行什么代码呢?这是一个设计错误
与其尝试重新创建类继承重写,您可能希望在这里将行为附加到实体类型。例如:
// Entities have a way to go()
protocol Entity {
static func go()
}
// And if they don't provide one, there's a default
extension Entity {
static func go() {
print("MyStruct default go()")
}
}
// FromSource_A and _B provide their own default ways to conform
protocol FromSource_A: Entity { }
protocol FromSource_B: Entity { }
extension FromSource_A {
static func go() {
print("MyStruct extension where T : FromSource_A")
}
}
extension FromSource_B {
static func go() {
print("MyStruct extension where T : FromSource_B")
}
}
// Apple and Orange conform, and take the default behaviors (they could provide their own)
struct Apple: FromSource_A { }
struct Orange: FromSource_B { }
// MyStruct (there's no need for a protocol) accepts a GenericArgument, but
// only to nail down what `T` is.
struct GenericArgument<T: Entity> { }
struct MyStruct<T: Entity> {
var genericArgument: GenericArgument<T>
func go () {
T.go()
}
}
// And the rest
func test<T: Entity> (argument: GenericArgument<T>) {
let myStruct = MyStruct<T>(genericArgument: argument)
myStruct.go()
}
let genericArgument = GenericArgument<Apple>()
test(argument: genericArgument) // MyStruct extension where T : FromSource_A
这可能会也可能不会像您预期的那样。我会努力摆脱所有的泛型和几乎所有的协议,并通过简单的结构和琐碎的协议这样做:
protocol Entity {}
protocol Source {
func go()
func makeEntity() -> Entity
}
struct Apple: Entity { }
struct Orange: Entity { }
struct Source_A: Source {
func go() { print("From A") }
func makeEntity() -> Entity { return Apple() }
}
struct Source_B: Source {
func go() { print("From B") }
func makeEntity() -> Entity { return Orange() }
}
struct GenericArgument {
let source: Source
}
struct MyStruct {
var genericArgument: GenericArgument
func go () {
genericArgument.source.go()
}
}
func test(argument: GenericArgument) {
let myStruct = MyStruct(genericArgument: argument)
myStruct.go()
}
let genericArgument = GenericArgument(source: Source_A())
test(argument: genericArgument)
您的问题可能实际上需要泛型,但是您应该从尽可能简单地编写代码开始(包括允许代码重复),然后寻找如何使用泛型消除重复。你不应该跳到泛型太快;我们大多数人都会选择错误的抽象。您似乎试图从泛型中创建类继承。那是不可能的。泛型不是动态调度的。这是故意的,允许进行更多的优化 使用
where
子句在默认实现上提供专门的扩展,就像您在这里所做的那样,只应该用于性能改进。如果编译器能够证明关于类型的某些东西(例如,它是一个双向集合而不是序列),那么提供一个更有效的算法来生成相同的输出可能会很有用。但是对MyStruct.go()
的所有调用都应该具有相同的语义(产生相同的输出)。关于调用哪个版本的go
的决定是在编译时根据编译时可用的信息做出的。test()
可能会从程序的其他部分以不同的类型调用,因此该函数无法专用于应用正确的where
子句。它必须假设允许的最一般情况
在这种特定情况下,如果我添加以下行,您希望发生什么情况:
extension Apple: FromSource_B {}
这是完全合法的,因为苹果符合源代码。我甚至可以在另一个模块中添加这行代码(在这里的所有内容都编译好之后)。那么应该运行什么代码呢?这是一个设计错误
与其尝试重新创建类继承重写,您可能希望在这里将行为附加到实体类型。例如:
// Entities have a way to go()
protocol Entity {
static func go()
}
// And if they don't provide one, there's a default
extension Entity {
static func go() {
print("MyStruct default go()")
}
}
// FromSource_A and _B provide their own default ways to conform
protocol FromSource_A: Entity { }
protocol FromSource_B: Entity { }
extension FromSource_A {
static func go() {
print("MyStruct extension where T : FromSource_A")
}
}
extension FromSource_B {
static func go() {
print("MyStruct extension where T : FromSource_B")
}
}
// Apple and Orange conform, and take the default behaviors (they could provide their own)
struct Apple: FromSource_A { }
struct Orange: FromSource_B { }
// MyStruct (there's no need for a protocol) accepts a GenericArgument, but
// only to nail down what `T` is.
struct GenericArgument<T: Entity> { }
struct MyStruct<T: Entity> {
var genericArgument: GenericArgument<T>
func go () {
T.go()
}
}
// And the rest
func test<T: Entity> (argument: GenericArgument<T>) {
let myStruct = MyStruct<T>(genericArgument: argument)
myStruct.go()
}
let genericArgument = GenericArgument<Apple>()
test(argument: genericArgument) // MyStruct extension where T : FromSource_A
这可能会也可能不会像您预期的那样。我会努力摆脱所有的泛型和几乎所有的协议,并通过简单的结构和琐碎的协议这样做:
protocol Entity {}
protocol Source {
func go()
func makeEntity() -> Entity
}
struct Apple: Entity { }
struct Orange: Entity { }
struct Source_A: Source {
func go() { print("From A") }
func makeEntity() -> Entity { return Apple() }
}
struct Source_B: Source {
func go() { print("From B") }
func makeEntity() -> Entity { return Orange() }
}
struct GenericArgument {
let source: Source
}
struct MyStruct {
var genericArgument: GenericArgument
func go () {
genericArgument.source.go()
}
}
func test(argument: GenericArgument) {
let myStruct = MyStruct(genericArgument: argument)
myStruct.go()
}
let genericArgument = GenericArgument(source: Source_A())
test(argument: genericArgument)
您的问题可能实际上需要泛型,但是您应该从尽可能简单地编写代码开始(包括允许代码重复),然后寻找如何使用泛型消除重复。你不应该跳到泛型太快;我们大多数人都会选择错误的抽象。扩展成员不是动态调度的。泛型在编译时解析。编译器只知道,
myStruct
对于任何可能的T
,都是泛型的,因此iIt选择go
的默认实现。扩展成员不会动态调度。泛型在编译时解析。编译器只知道,myStruct
对于任何可能的T
都是泛型的,因此iIt选择了go
的默认实现。感谢您的解释和指导!感谢您的解释和指导!