【问题标题】:protocol associated type typealias assignment compile error协议关联类型类型别名赋值编译错误
【发布时间】:2015-07-22 11:49:52
【问题描述】:

以下代码:

protocol SomeProtocol {
    typealias SomeType = Int // used typealias-assignment

    func someFunc(someVar: SomeType)
}

class SomeClass: SomeProtocol {
    func someFunc(someVar: SomeType) {
        print(someVar)
    }
}

给出编译时错误:

使用未声明的类型“SomeType”

typealias SomeType = Double 添加到SomeClass 可以解决错误。

问题是,协议关联类型声明的 typealias-assignment 部分(顺便说一句是可选的)有什么意义?

【问题讨论】:

    标签: swift swift2 protocols


    【解决方案1】:

    在这种情况下,将Int 分配给typealias 等于没有分配,因为它会被您的符合类型覆盖:

    // this declaration is equal since you HAVE TO provide the type for SomeType
    protocol SomeProtocol {
        typealias SomeType
    
        func someFunc(someVar: SomeType)
    }
    

    这样的赋值为SomeType 提供了一个默认类型,它会被SomeClass 中的实现覆盖,但它对于协议扩展特别有用:

    protocol Returnable {
        typealias T = Int // T is by default of type Int
        func returnValue(value: T) -> T
    }
    
    extension Returnable {
        func returnValue(value: T) -> T {
            return value
        }
    }
    
    struct AStruct: Returnable {}
    
    AStruct().returnValue(3) // default signature: Int -> Int
    

    只有遵守协议,不指定T的类型,才能免费获得该功能。如果您想设置自己的类型,请在结构体中写入typealias T = String // or any other type

    关于提供的代码示例的一些附加说明

    您解决了问题,因为您明确说明了参数的类型。 Swift 还会推断您使用的类型:

    class SomeClass: SomeProtocol {
        func someFunc(someVar: Double) {
            print(someVar)
        }
    }
    

    所以协议的SomeType被推断为Double

    另一个例子,你可以看到类声明中的SomeType 没有引用协议:

    class SomeClass: SomeProtocol {
        typealias Some = Int
        func someFunc(someVar: Some) {
            print(someVar)
        }
    }
    
    // check the type of SomeType of the protocol
    // dynamicType returns the current type and SomeType is a property of it
    SomeClass().dynamicType.SomeType.self // Int.Type
    // SomeType gets inferred form the function signature
    

    但是,如果你这样做:

    protocol SomeProtocol {
        typealias SomeType: SomeProtocol
    
        func someFunc(someVar: SomeType)
    }
    

    SomeType 必须是 SomeProtocol 类型,它可以用于更明确的抽象和更多的静态代码,而这个:

    protocol SomeProtocol {
        func someFunc(someVar: SomeProtocol)
    }
    

    将被动态调度。

    【讨论】:

    • 看,我没有要求任何人回答为什么在类中声明 typealias 可以解决错误。在我创建这个主题之前,我已经意识到了这一点。我问 typealias 分配在协议中的实际应用是什么。换句话说,此功能解决了哪些具体限制?
    • @mesmerizingsnow 这只是附加信息。但我也给出了答案:赋值对没有赋值的行为没有影响(typealias SomeType = Int 等于typealias SomeType
    • 我凭经验得到了这个答案。你有什么东西来支持你的答案,比如 developer.apple.com 论坛的链接或来自一些博客、推特或其他东西的引用吗?
    • 经过一些研究(不幸的是我没有找到关于它的文档),我发现了作业的实际含义(编辑了我的答案)。但是,如果您保持协议不变,则分配是多余的。
    • 我明白你的意思,你可能是对的。我接受您的回答为有效回答。谢谢!
    【解决方案2】:

    协议中有关“associated types”的文档中有一些重要信息。

    它们在整个标准库中的使用非常丰富,例如参考SequenceType 协议,它为Generator 声明了一个typealias(并指定它符合GeneratorType)。这允许协议声明引用该别名类型。

    在您使用 typealias SomeType = Int 的情况下,您的意思可能是“我希望 SomeType 被限制为类似整数的行为,因为我的协议方法将依赖于该约束” - 在在这种情况下,您可能希望在您的协议中使用typealias SomeType: IntegerType,然后在您的课程中继续为符合IntegerType 的别名分配一个类型。

    更新

    在向 Apple 提出了一个错误并围绕它进行了广泛的讨论后,我已经了解了这个问题的核心问题:

    当遵守协议时,您不能直接引用仅在该协议中声明的关联类型

    (但是请注意,当扩展协议时,相关类型可用,正如您所期望的那样)

    所以在你的初始代码示例中:

    protocol SomeProtocol {
        typealias SomeType = Int
        func someFunc(someVar: SomeType)
    }
    
    class SomeClass: SomeProtocol {
        func someFunc(someVar: SomeType) {  // use of undeclared type "SomeType"
            print(someVar)
        }
    }
    

    ...错误回复:“使用未声明的类型”是正确的,您的类SomeClass 没有声明类型SomeType

    但是,SomeProtocol 的扩展可以访问关联的类型,并且可以在提供实现时引用它:

    (请注意,这需要使用where 子句来定义对关联类型的要求)

    protocol SomeProtocol {
        typealias SomeType = Int
        func someFunc(someVar: SomeType)
    }
    
    extension SomeProtocol where SomeType == Int {
        func someFunc(someVar: SomeType) {
            print("1 + \(someVar) = \(1 + someVar)")
        }
    }
    
    class SomeClass: SomeProtocol {}
    
    SomeClass().someFunc(3)  // => "1 + 3 = 4"
    

    【讨论】:

    • 在我的例子中,我使用“Int”分配只是为了测试这个功能。我坐在我的笔记本电脑上阅读官方 Apple 'Swift Programming Guide' 参考的'Language Reference',进入'Declaration' 部分并找到带有关联类型声明语法链接的协议声明链接。我发现在声明类型别名时可以使用类型别名赋值。虽然它是可选的。
    【解决方案3】:

    great article 实际上可以回答您的问题。我建议每个人都阅读它以了解类型别名和使用它时出现的一些更高级的内容。

    来自网站的引用:

    从概念上讲,Swift 中没有通用协议。但是通过使用 typealias 我们可以为另一个类型声明一个必需的别名。

    【讨论】:

    • 在文章中他们没有使用 typealias 赋值。他们虽然使用了类型继承子句,但这并不能回答我的问题。
    猜你喜欢
    • 1970-01-01
    • 2021-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-06
    相关资源
    最近更新 更多