Swift 为什么可以';泛型结构类型不能由它初始化时使用的参数推断出来吗?

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

我不确定我是否能足够清楚地描述这个问题,但我已经成功地制作了一个小的(足够)可重复的例子。在这段代码中,我为源a中的一个实体和源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
的默认实现。感谢您的解释和指导!感谢您的解释和指导!