【问题标题】:How do you implement protocol methods that return covariant Selfs?你如何实现返回协变 Selfs 的协议方法?
【发布时间】:2016-01-05 02:26:42
【问题描述】:

错误:协议“协议”要求“实例”不能被非最终类(“类”)满足,因为它在非参数、非结果类型位置使用“自我”

protocol Protocol {
    var instance: Self {get}
}

class Class: Protocol {
    var instance: Class {return Subclass()}
}

class Subclass: Class {}

这是我在 C# 中表达我想要的内容的方式。 (据我所知,C# 没有办法强制泛型参数“Self”实际上是我们从 Swift 中知道的 Self,但它作为文档的功能足够好,应该让我做正确的事情。)

interface Protocol<Self> where Self: Protocol<Self> {
    Self instance {get;}
}

class Class: Protocol<Class> {
    public Class instance {get {return new Subclass();}}
}

class Subclass: Class {}

…在 Swift 的未来版本中可能会是什么样子:

protocol Protocol {
    typealias FinalSelf: Protocol where FinalSelf.FinalSelf == FinalSelf

    var instance: FinalSelf {get}
}

class Class: Protocol {
    var instance: Class {return Subclass()}
}

class Subclass: Class {}

我如何模拟与我的问题相关的部分:

protocol Protocol: ProtocolInstance {
    static var instance: ProtocolInstance {get}
}

protocol ProtocolInstance {}


class Class: Protocol {
    static var instance: ProtocolInstance {return Subclass()}
}

class Subclass: Class {}

而且,这是我认为与我的代码相关的部分:

protocol Protocol {
    static var ????: Self? {get} // an existing instance? 
    static var ????: Self {get}  // a new instance

    func instanceFunc()
}

extension Protocol {
    static func staticFunc() {
        (???? ?? ????).instanceFunc()
    }
}

【问题讨论】:

  • 看起来像一个错误——您可以使类似的结构适用于实例方法,但不适用于类方法或静态属性。 Have you filed it?
  • 我也无法编译。已编辑。
  • 我相信您从 C# 更新的示例代码与我的 typealias InstanceType 代码完全相同。有一些行为差异吗?如果您在 C# 中调用 Subclass.instance(),返回类型(不是实现,而是变量类型)不会是 Class
  • 问题出在协议的其他地方,我必须使用实例的返回值。我不需要特别说明它是 Self 的子类,但我确实需要它来至少实现 Protocol。 C# 代码的限制使得除了返回 Self 或其子类之外的其他事情变得如此复杂,以至于我什至不知道它是否可能。

标签: swift inheritance protocols


【解决方案1】:

如果您不想像这样为每个子类实际返回Self,这实际上是有意义且有效的:

protocol Protocol : class {
    typealias Sub : Self
    var instance: Sub {get}
}

这意味着你的协议定义了一个类型别名,它必须是它自己的子类。以下代码可以正常工作:

class Class: Protocol {
    var instance: Class {return Subclass()}
}

class Subclass: Class {}

Class().instance    // Returns SubClass()

但是上面的代码没有编译错误

error: inheritance from non-protocol, non-class type '`Self`'

我认为这是一个错误,因为Self 被声明为类类型。但是,您可以以某种方式使其像这样工作:

protocol Protocol : class {
    typealias Sub : Class
    var instance: Sub {get}
}

但是你没有太多的协议本身,因为只有类本身应该符合它。

【讨论】:

    【解决方案2】:

    正如它所说,你不能这样做,这是有充分理由的。你无法证明你会信守诺言。考虑一下:

    class AnotherSubclass: Class {}
    let x = AnotherSubclass().instance
    

    所以根据您的协议,x 应该是 AnotherSubclass(即 Self)。但它实际上是Subclass,这是一个完全不同的类型。除非类是final,否则你无法解决这个悖论。这不是 Swift 的限制。这种限制存在于任何正确的类型系统中,因为它允许类型矛盾。

    另一方面,您可以做的事情是保证instance 在所有子类(即超类)中返回某种一致的类型。您可以使用关联类型执行此操作:

    protocol Protocol {
        typealias InstanceType
        var instance: InstanceType {get}
    }
    
    class Class: Protocol {
        var instance: Class {return Subclass()}
    }
    
    class Subclass: Class {}
    class AnotherSubclass: Class {}
    let x = AnotherSubclass().instance
    

    现在x 无疑是Class 类型。 (它也恰好是随机的其他子类,这有点奇怪,但这就是代码所说的。)

    顺便说一句,所有这些通常表明您在不应该使用子类化时使用了子类化。组合和协议可能会在 Swift 中更好地解决这个问题。问问自己是否有任何理由 Subclass 实际上需要成为 Class 的子类。会不会是符合相同协议的独立类型?当您摆脱子类并专注于协议时,各种问题都会消失。


    我一直在考虑这个问题,并且可能有一种方法可以获得您正在寻找的东西。与其说所有子类都实现instance,不如附加instance 作为扩展。如果您想返回其他内容,您仍然可以覆盖它。

    protocol Protocol {
        init()
    }
    
    class Class: Protocol {
        required init() {}
        var instance: Class { return Subclass() }
    }
    
    extension Protocol {
        var instance: Self { return self.dynamicType.init() }
    }
    
    class Subclass: Class {}
    

    这避免了继承问题(您不能以这种方式创建相同的“AnotherClass 返回错误类型”)。

    【讨论】:

    • 我想做的承诺是实例返回一个 Self 或一个 T:Self;如果说“自我”没有说明清楚,我深表歉意,但如果有表达我想要的语法,我还不知道。
    • 你不能在课堂上信守承诺。请参阅我的 AnotherSubclass 示例。这不是语法问题。这是类型的问题。没有办法毫无矛盾地表达你所说的。如果ClassSubclassAnotherSubclass 都是结构(或者只是没有继承),那么它们都可以一致地实现instance。但是当你混合继承时,你就不能再信守诺言了。
    • 谢谢。我现在看到问题来自于无法表达一个类实现了一个协议,而不说它的子类也实现了该协议。 (我知道这一点,但直到现在还没有遇到由此产生的问题。)我认为随着协议概念的发展,这将在未来得到解决。另外,据我所知,为了使用抽象的核心数据实体,我必须处理继承。
    • 您似乎了解如何以我还不知道的方式证明这些关系的属性。我很想知道我上面添加的“FinalSelf:FinalSelf.FinalSelf == FinalSelf 的协议”(在 C# 中编译)中的 FinalSelf 是否意味着“我是的结构或类或派生自我的类”之外的任何东西.定义了这样一个东西之后,你可以在需要“协议”的地方使用它,但至于第一个定义,我不知道,但它似乎意味着我正在谈论的这个自我的替代概念。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-25
    • 2021-02-24
    相关资源
    最近更新 更多