繁体   English   中英

为什么不能通过初始化它的参数来推断泛型结构类型?

[英]Why can't a generic struct type be inferred by the argument it is initialized with?

我不确定我是否可以足够清楚地描述这个问题,但已经设法制作了一个小(足够)可重现的例子。 在这段代码中,我为来自源 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添加一个也是泛型的属性并使用传递给它的GenericArgument初始化它的具体实例时,我希望具体类型GenericArgument<Apple>将告知泛型MyStruct T是什么,并允许在MyStruct上调用正确的扩展名。

....

struct MyStruct<T: Entity>: StructProtocol {
    var genericArgument: GenericArgument<T> // Adding generic argument that takes T from MyStruct declaration

    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")
    }
}

/// Now introduce passing type between structs

struct GenericArgument<T: Entity> { }

func test<T: Entity> (argument: GenericArgument<T>) {
    let myStruct = MyStruct<T>(genericArgument: argument)
    myStruct.go()
}

let genericArgument = GenericArgument<Apple>()

test(argument: genericArgument) // <- Output: "MyStruct default go()"

而是调用MyStruct的默认方法实现。

据我所见,当我引入另一个层以通过( func test<T: Entity> (argument: GenericArgument<T>) {} )传递通用信息时,问题就出现了,在给定具体参数的情况下,接收者可以' t弄清楚它的泛型类型已经填充了什么。

当 MyStruct 的类型由初始化参数推断时,为什么MyStruct不调用正确扩展中的方法?

您似乎正在尝试从 generics 中创建 class inheritance。 那是不可能的。 Generics 不是动态调度的。 这是故意的,并允许更多的优化。

像您在此处所做的那样,使用where子句对默认实现提供专门的扩展应该只是为了提高性能。 如果编译器可以证明有关类型的某些信息(例如,它是双向集合而不是序列),那么提供更有效的算法来生成相同的 output 会很有用。 但是对MyStruct.go()的所有调用都应该具有相同的语义(产生相同的输出)。 仅根据编译时可用的信息在编译时决定调用哪个版本的go 可能会从程序的其他部分以不同的类型调用test() ,因此 function 不能专门用于应用正确的where子句。 它必须假设允许的最一般情况。

在这种特定情况下,如果我添加以下行,您会期望发生什么:

extension Apple: FromSource_B {}

这是完全合法的,因为Apple符合FromSource_B 我什至可以在另一个模块中添加这行代码(在编译完这里的所有内容之后)。 那么应该运行什么代码呢? 这是指向一个设计错误。

与其尝试重新创建类继承覆盖,您可能希望在这里将行为附加到 Entity 类型。 例如:

// 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

你仍然需要小心一点。 在某些情况下它可能会破裂。 例如,如果有人在另一个模块中编写此代码怎么办:

extension Apple {
    static func go() { print("This is an Apple.") }
}

这可能会或可能不会像您预期的那样运行。 我会努力摆脱所有 generics 以及几乎所有协议,并使用简单的结构和琐碎的协议来做到这一点:

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)

您的问题可能实际上需要 generics 在这里,但您应该从尽可能简单地写出代码开始(包括允许它重复),然后寻找如何使用 generics 删除该重复。 你不应该太快跳到 generics; 我们大多数人都会选择错误的抽象。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM